cwtools-mcp 0.2.3 → 0.2.5
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 +2 -3
- package/dist/config.js +1 -1
- package/dist/cwtools-mcp.cjs +398 -195
- package/dist/hosts/lspProcessHost.js +2 -4
- package/dist/hosts/nodeHostServices.js +1 -1
- package/dist/hosts/projectDetect.js +1 -1
- package/dist/hosts/projectSettings.js +1 -5
- package/dist/hosts/revalidation.js +1 -1
- package/dist/mcp/bridgeProxy.js +2 -2
- package/dist/server.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -95,9 +95,8 @@ intentionally does not silently fall back to a separate language server.
|
|
|
95
95
|
|
|
96
96
|
### Optional Standalone Mode
|
|
97
97
|
|
|
98
|
-
Use standalone mode only when you explicitly want the
|
|
99
|
-
|
|
100
|
-
state.
|
|
98
|
+
Use standalone mode only when you explicitly want the MCP process to start its
|
|
99
|
+
own CWTools language server and build an independent diagnostic state.
|
|
101
100
|
|
|
102
101
|
```sh
|
|
103
102
|
cwtools-mcp --standalone --workspace /path/to/mod --game stellaris --stdio
|
package/dist/config.js
CHANGED
|
@@ -189,7 +189,7 @@ function helpText() {
|
|
|
189
189
|
' or --workspace when supplied) must match the bridge workspace exactly. If the',
|
|
190
190
|
' compatible host is closed or the workspace does not match, tool calls return an',
|
|
191
191
|
' actionable unavailable error.',
|
|
192
|
-
' Use --standalone only when you intentionally want the
|
|
192
|
+
' Use --standalone only when you intentionally want the standalone self-hosted LSP mode.',
|
|
193
193
|
'',
|
|
194
194
|
'Vanilla data (needed for vanilla IDs and correct mod-vs-vanilla diagnostics):',
|
|
195
195
|
' (auto) If neither flag is given, the VS Code cwtools extension cache',
|
package/dist/cwtools-mcp.cjs
CHANGED
|
@@ -197,7 +197,7 @@ ${helpText()}`);
|
|
|
197
197
|
" or --workspace when supplied) must match the bridge workspace exactly. If the",
|
|
198
198
|
" compatible host is closed or the workspace does not match, tool calls return an",
|
|
199
199
|
" actionable unavailable error.",
|
|
200
|
-
" Use --standalone only when you intentionally want the
|
|
200
|
+
" Use --standalone only when you intentionally want the standalone self-hosted LSP mode.",
|
|
201
201
|
"",
|
|
202
202
|
"Vanilla data (needed for vanilla IDs and correct mod-vs-vanilla diagnostics):",
|
|
203
203
|
" (auto) If neither flag is given, the VS Code cwtools extension cache",
|
|
@@ -458,14 +458,16 @@ var require_mcpTools = __commonJS({
|
|
|
458
458
|
"properties": {
|
|
459
459
|
"file": {
|
|
460
460
|
"type": "string",
|
|
461
|
-
"description": "Absolute
|
|
461
|
+
"description": "Absolute path inside the workspace or a configured game installation."
|
|
462
462
|
},
|
|
463
463
|
"line": {
|
|
464
|
-
"type": "
|
|
464
|
+
"type": "integer",
|
|
465
|
+
"minimum": 0,
|
|
465
466
|
"description": "Line number (0-based)"
|
|
466
467
|
},
|
|
467
468
|
"column": {
|
|
468
|
-
"type": "
|
|
469
|
+
"type": "integer",
|
|
470
|
+
"minimum": 0,
|
|
469
471
|
"description": "Column number (0-based)"
|
|
470
472
|
}
|
|
471
473
|
},
|
|
@@ -473,7 +475,8 @@ var require_mcpTools = __commonJS({
|
|
|
473
475
|
"file",
|
|
474
476
|
"line",
|
|
475
477
|
"column"
|
|
476
|
-
]
|
|
478
|
+
],
|
|
479
|
+
"additionalProperties": false
|
|
477
480
|
}
|
|
478
481
|
},
|
|
479
482
|
"registry": {
|
|
@@ -558,10 +561,6 @@ var require_mcpTools = __commonJS({
|
|
|
558
561
|
"toolResult": {
|
|
559
562
|
"type": "object",
|
|
560
563
|
"description": "Optional raw failed tool result to classify without querying diagnostics again."
|
|
561
|
-
},
|
|
562
|
-
"reflection": {
|
|
563
|
-
"type": "string",
|
|
564
|
-
"description": "Optional legacy free-form analysis from the model. Kept for backward compatibility; the host still returns deterministic routing advice."
|
|
565
564
|
}
|
|
566
565
|
},
|
|
567
566
|
"required": []
|
|
@@ -579,7 +578,7 @@ var require_mcpTools = __commonJS({
|
|
|
579
578
|
{
|
|
580
579
|
"tool": {
|
|
581
580
|
"name": "query_project_profile",
|
|
582
|
-
"description": "Read the /init-generated Agent project profile from .cwtools/project/profile.json. Use this before broad scans to get workspace type, key directories, localisation languages/encoding, namespaces, workflow routing, validation hints, and
|
|
581
|
+
"description": "Read the /init-generated Agent project profile from .cwtools/project/profile.json. Use this before broad scans to get workspace type, key directories, localisation languages/encoding, namespaces, workflow routing, validation hints, and targeted guidance cards.",
|
|
583
582
|
"inputSchema": {
|
|
584
583
|
"type": "object",
|
|
585
584
|
"properties": {
|
|
@@ -593,29 +592,25 @@ var require_mcpTools = __commonJS({
|
|
|
593
592
|
"identifiers",
|
|
594
593
|
"validation",
|
|
595
594
|
"compatibility",
|
|
596
|
-
"
|
|
595
|
+
"guidanceCards",
|
|
597
596
|
"all"
|
|
598
597
|
],
|
|
599
598
|
"description": "Targeted profile section to return. Default summary. Use all only when you need the whole profile."
|
|
600
599
|
},
|
|
601
|
-
"
|
|
600
|
+
"guidance": {
|
|
602
601
|
"type": "string",
|
|
603
602
|
"enum": [
|
|
604
|
-
"
|
|
605
|
-
"
|
|
606
|
-
"
|
|
607
|
-
"general",
|
|
608
|
-
"utility",
|
|
603
|
+
"implementation",
|
|
604
|
+
"planning",
|
|
605
|
+
"exploration",
|
|
609
606
|
"review",
|
|
610
|
-
"
|
|
611
|
-
"
|
|
612
|
-
"
|
|
613
|
-
"
|
|
614
|
-
"
|
|
615
|
-
"script",
|
|
616
|
-
"asset"
|
|
607
|
+
"utility",
|
|
608
|
+
"localisation",
|
|
609
|
+
"assets",
|
|
610
|
+
"coordination",
|
|
611
|
+
"paradox_coordination"
|
|
617
612
|
],
|
|
618
|
-
"description": "Optional
|
|
613
|
+
"description": "Optional guidance card to return for the current task concern."
|
|
619
614
|
}
|
|
620
615
|
},
|
|
621
616
|
"required": []
|
|
@@ -788,7 +783,7 @@ var require_mcpTools = __commonJS({
|
|
|
788
783
|
},
|
|
789
784
|
"file": {
|
|
790
785
|
"type": "string",
|
|
791
|
-
"description": "Optional workspace-relative or absolute
|
|
786
|
+
"description": "Optional workspace-relative path or absolute path inside the workspace/configured game installation used to restrict entry points."
|
|
792
787
|
},
|
|
793
788
|
"typeName": {
|
|
794
789
|
"type": "string",
|
|
@@ -857,7 +852,7 @@ var require_mcpTools = __commonJS({
|
|
|
857
852
|
},
|
|
858
853
|
"file": {
|
|
859
854
|
"type": "string",
|
|
860
|
-
"description": "Caller file; narrows to one invocation."
|
|
855
|
+
"description": "Caller file inside the workspace or a configured game installation; narrows to one invocation."
|
|
861
856
|
},
|
|
862
857
|
"line": {
|
|
863
858
|
"type": "number",
|
|
@@ -883,13 +878,13 @@ var require_mcpTools = __commonJS({
|
|
|
883
878
|
{
|
|
884
879
|
"tool": {
|
|
885
880
|
"name": "analyze_pdx_flow",
|
|
886
|
-
"description": "Static
|
|
881
|
+
"description": "Static flow model for a file or definition: dynamic event subtypes, inline-instantiated state/calls, cross-event and scripted-effect state flow, pulse/on_action/scripted-effect cost propagation, traversals, loops, and typed gameplay edges. Costs are relative static scores, not runtime predictions.",
|
|
887
882
|
"inputSchema": {
|
|
888
883
|
"type": "object",
|
|
889
884
|
"properties": {
|
|
890
885
|
"file": {
|
|
891
886
|
"type": "string",
|
|
892
|
-
"description": "Optional
|
|
887
|
+
"description": "Optional path inside the workspace or a configured game installation; when omitted a definitionId must be given."
|
|
893
888
|
},
|
|
894
889
|
"definitionId": {
|
|
895
890
|
"type": "string",
|
|
@@ -1014,13 +1009,13 @@ var require_mcpTools = __commonJS({
|
|
|
1014
1009
|
{
|
|
1015
1010
|
"tool": {
|
|
1016
1011
|
"name": "get_pdx_block",
|
|
1017
|
-
"description": "Extract exactly one complete AST block by symbol name. Works with .txt (events, common), .gui (containerWindowType by name), and .gfx (pdxmesh by name). Returns 1-based startLine/endLine that can be passed directly to replace_lines. If the symbol is not found, the error response includes a full list of available symbols with line ranges so you can retry with the correct name.",
|
|
1012
|
+
"description": "Extract exactly one complete AST block by symbol name from the workspace or a configured game installation. Works with .txt (events, common), .gui (containerWindowType by name), and .gfx (pdxmesh by name). Returns 1-based startLine/endLine that can be passed directly to replace_lines. If the symbol is not found, the error response includes a full list of available symbols with line ranges so you can retry with the correct name.",
|
|
1018
1013
|
"inputSchema": {
|
|
1019
1014
|
"type": "object",
|
|
1020
1015
|
"properties": {
|
|
1021
1016
|
"file": {
|
|
1022
1017
|
"type": "string",
|
|
1023
|
-
"description": "Absolute
|
|
1018
|
+
"description": "Absolute path inside the workspace or a configured game installation."
|
|
1024
1019
|
},
|
|
1025
1020
|
"symbol": {
|
|
1026
1021
|
"type": "string",
|
|
@@ -1051,7 +1046,7 @@ var require_mcpTools = __commonJS({
|
|
|
1051
1046
|
"properties": {
|
|
1052
1047
|
"file": {
|
|
1053
1048
|
"type": "string",
|
|
1054
|
-
"description": "Absolute file path
|
|
1049
|
+
"description": "Absolute file path inside the workspace or a configured game installation."
|
|
1055
1050
|
},
|
|
1056
1051
|
"line": {
|
|
1057
1052
|
"type": "number",
|
|
@@ -1091,7 +1086,7 @@ var require_mcpTools = __commonJS({
|
|
|
1091
1086
|
"properties": {
|
|
1092
1087
|
"file": {
|
|
1093
1088
|
"type": "string",
|
|
1094
|
-
"description": "Absolute
|
|
1089
|
+
"description": "Absolute path inside the workspace or a configured game installation."
|
|
1095
1090
|
}
|
|
1096
1091
|
},
|
|
1097
1092
|
"required": [
|
|
@@ -1140,33 +1135,34 @@ var require_mcpTools = __commonJS({
|
|
|
1140
1135
|
},
|
|
1141
1136
|
{
|
|
1142
1137
|
"tool": {
|
|
1143
|
-
"name": "
|
|
1144
|
-
"description": "
|
|
1138
|
+
"name": "go_to_definition",
|
|
1139
|
+
"description": "Find a definition by exact symbol name or by a 0-based file position. Uses CWTools semantic lookup when available and falls back to the active VS Code language provider.",
|
|
1145
1140
|
"inputSchema": {
|
|
1146
1141
|
"type": "object",
|
|
1147
1142
|
"properties": {
|
|
1143
|
+
"symbolName": {
|
|
1144
|
+
"type": "string",
|
|
1145
|
+
"description": "Exact symbol name. Use this instead of file/line/column when the identifier is known."
|
|
1146
|
+
},
|
|
1148
1147
|
"file": {
|
|
1149
1148
|
"type": "string",
|
|
1150
|
-
"description": "Absolute file path"
|
|
1149
|
+
"description": "Absolute file path inside the workspace or a configured game installation."
|
|
1151
1150
|
},
|
|
1152
1151
|
"line": {
|
|
1153
1152
|
"type": "number",
|
|
1154
|
-
"description": "Line number (0-based)"
|
|
1153
|
+
"description": "Line number (0-based)."
|
|
1155
1154
|
},
|
|
1156
1155
|
"column": {
|
|
1157
1156
|
"type": "number",
|
|
1158
|
-
"description": "Column number (0-based)"
|
|
1157
|
+
"description": "Column number (0-based)."
|
|
1159
1158
|
}
|
|
1160
1159
|
},
|
|
1161
|
-
"required": [
|
|
1162
|
-
|
|
1163
|
-
"line",
|
|
1164
|
-
"column"
|
|
1165
|
-
]
|
|
1160
|
+
"required": [],
|
|
1161
|
+
"additionalProperties": false
|
|
1166
1162
|
}
|
|
1167
1163
|
},
|
|
1168
1164
|
"registry": {
|
|
1169
|
-
"name": "
|
|
1165
|
+
"name": "go_to_definition",
|
|
1170
1166
|
"isWrite": false,
|
|
1171
1167
|
"isReadOnly": true,
|
|
1172
1168
|
"effect": "workspace_read",
|
|
@@ -1176,53 +1172,38 @@ var require_mcpTools = __commonJS({
|
|
|
1176
1172
|
},
|
|
1177
1173
|
{
|
|
1178
1174
|
"tool": {
|
|
1179
|
-
"name": "
|
|
1180
|
-
"description": "Find
|
|
1181
|
-
"inputSchema": {
|
|
1182
|
-
"type": "object",
|
|
1183
|
-
"properties": {
|
|
1184
|
-
"symbolName": {
|
|
1185
|
-
"type": "string",
|
|
1186
|
-
"description": 'The exact name of the symbol to find (e.g. "kuat_has_psionic_research", "distar.001")'
|
|
1187
|
-
}
|
|
1188
|
-
},
|
|
1189
|
-
"required": [
|
|
1190
|
-
"symbolName"
|
|
1191
|
-
]
|
|
1192
|
-
}
|
|
1193
|
-
},
|
|
1194
|
-
"registry": {
|
|
1195
|
-
"name": "query_definition_by_name",
|
|
1196
|
-
"isWrite": false,
|
|
1197
|
-
"isReadOnly": true,
|
|
1198
|
-
"effect": "workspace_read",
|
|
1199
|
-
"riskLevel": 0,
|
|
1200
|
-
"concurrencyClass": "parallel"
|
|
1201
|
-
}
|
|
1202
|
-
},
|
|
1203
|
-
{
|
|
1204
|
-
"tool": {
|
|
1205
|
-
"name": "query_references",
|
|
1206
|
-
"description": "Find references to an identifier through the active language provider, with a bounded workspace text-search fallback.",
|
|
1175
|
+
"name": "find_references",
|
|
1176
|
+
"description": "Find references by exact identifier or by a 0-based file position. Identifier lookup uses workspace symbols plus a bounded text fallback; position lookup uses the active VS Code language provider.",
|
|
1207
1177
|
"inputSchema": {
|
|
1208
1178
|
"type": "object",
|
|
1209
1179
|
"properties": {
|
|
1210
1180
|
"identifier": {
|
|
1211
1181
|
"type": "string",
|
|
1212
|
-
"description": "
|
|
1182
|
+
"description": "Exact identifier. Use this instead of file/line/column when the name is known."
|
|
1213
1183
|
},
|
|
1214
1184
|
"file": {
|
|
1215
1185
|
"type": "string",
|
|
1216
|
-
"description": "
|
|
1186
|
+
"description": "Absolute file path inside the workspace or a configured game installation."
|
|
1187
|
+
},
|
|
1188
|
+
"line": {
|
|
1189
|
+
"type": "number",
|
|
1190
|
+
"description": "Line number (0-based)."
|
|
1191
|
+
},
|
|
1192
|
+
"column": {
|
|
1193
|
+
"type": "number",
|
|
1194
|
+
"description": "Column number (0-based)."
|
|
1195
|
+
},
|
|
1196
|
+
"limit": {
|
|
1197
|
+
"type": "number",
|
|
1198
|
+
"description": "Maximum references to return (default 100, max 500)."
|
|
1217
1199
|
}
|
|
1218
1200
|
},
|
|
1219
|
-
"required": [
|
|
1220
|
-
|
|
1221
|
-
]
|
|
1201
|
+
"required": [],
|
|
1202
|
+
"additionalProperties": false
|
|
1222
1203
|
}
|
|
1223
1204
|
},
|
|
1224
1205
|
"registry": {
|
|
1225
|
-
"name": "
|
|
1206
|
+
"name": "find_references",
|
|
1226
1207
|
"isWrite": false,
|
|
1227
1208
|
"isReadOnly": true,
|
|
1228
1209
|
"effect": "workspace_read",
|
|
@@ -1375,7 +1356,7 @@ var require_mcpTools = __commonJS({
|
|
|
1375
1356
|
"properties": {
|
|
1376
1357
|
"file": {
|
|
1377
1358
|
"type": "string",
|
|
1378
|
-
"description": "Absolute file
|
|
1359
|
+
"description": "Absolute path to a parsed file inside the workspace or a configured game installation."
|
|
1379
1360
|
}
|
|
1380
1361
|
},
|
|
1381
1362
|
"required": [
|
|
@@ -1444,7 +1425,7 @@ var require_mcpTools = __commonJS({
|
|
|
1444
1425
|
"properties": {
|
|
1445
1426
|
"file": {
|
|
1446
1427
|
"type": "string",
|
|
1447
|
-
"description": "
|
|
1428
|
+
"description": "Workspace-relative path or absolute path inside the workspace/configured game installation."
|
|
1448
1429
|
}
|
|
1449
1430
|
},
|
|
1450
1431
|
"required": [
|
|
@@ -1470,7 +1451,7 @@ var require_mcpTools = __commonJS({
|
|
|
1470
1451
|
"properties": {
|
|
1471
1452
|
"file": {
|
|
1472
1453
|
"type": "string",
|
|
1473
|
-
"description": "
|
|
1454
|
+
"description": "Workspace-relative path or absolute path inside the workspace/configured game installation."
|
|
1474
1455
|
}
|
|
1475
1456
|
},
|
|
1476
1457
|
"required": [
|
|
@@ -1530,7 +1511,7 @@ var require_mcpTools = __commonJS({
|
|
|
1530
1511
|
},
|
|
1531
1512
|
"file": {
|
|
1532
1513
|
"type": "string",
|
|
1533
|
-
"description": "
|
|
1514
|
+
"description": "Workspace-relative path or absolute path inside the workspace/configured game installation; lists its Effects with cursor pagination."
|
|
1534
1515
|
},
|
|
1535
1516
|
"limit": {
|
|
1536
1517
|
"type": "number",
|
|
@@ -1562,7 +1543,7 @@ var require_mcpTools = __commonJS({
|
|
|
1562
1543
|
"properties": {
|
|
1563
1544
|
"file": {
|
|
1564
1545
|
"type": "string",
|
|
1565
|
-
"description": "
|
|
1546
|
+
"description": "Workspace-relative path or absolute path inside the workspace/configured game installation."
|
|
1566
1547
|
}
|
|
1567
1548
|
},
|
|
1568
1549
|
"required": [
|
|
@@ -1592,7 +1573,7 @@ var require_mcpTools = __commonJS({
|
|
|
1592
1573
|
},
|
|
1593
1574
|
"file": {
|
|
1594
1575
|
"type": "string",
|
|
1595
|
-
"description": "
|
|
1576
|
+
"description": "Workspace-relative path or absolute path inside the workspace/configured game installation."
|
|
1596
1577
|
}
|
|
1597
1578
|
},
|
|
1598
1579
|
"required": []
|
|
@@ -1709,10 +1690,9 @@ var require_readiness = __commonJS({
|
|
|
1709
1690
|
"parse_pdx_fragment",
|
|
1710
1691
|
"query_scope",
|
|
1711
1692
|
"get_completion_at",
|
|
1712
|
-
"
|
|
1713
|
-
"query_definition_by_name",
|
|
1693
|
+
"go_to_definition",
|
|
1714
1694
|
"explore_pdx_project",
|
|
1715
|
-
"
|
|
1695
|
+
"find_references",
|
|
1716
1696
|
"get_diagnostics",
|
|
1717
1697
|
"query_scripted_effects",
|
|
1718
1698
|
"query_scripted_triggers",
|
|
@@ -1787,11 +1767,10 @@ var require_vanillaCache = __commonJS({
|
|
|
1787
1767
|
"query_types",
|
|
1788
1768
|
"get_completion_at",
|
|
1789
1769
|
"query_scope",
|
|
1790
|
-
"
|
|
1791
|
-
"query_definition_by_name",
|
|
1770
|
+
"go_to_definition",
|
|
1792
1771
|
"explore_pdx_project",
|
|
1793
1772
|
"query_project_knowledge",
|
|
1794
|
-
"
|
|
1773
|
+
"find_references",
|
|
1795
1774
|
"get_diagnostics",
|
|
1796
1775
|
"query_scripted_effects",
|
|
1797
1776
|
"query_scripted_triggers",
|
|
@@ -1903,7 +1882,7 @@ var require_diagnosticRouting = __commonJS({
|
|
|
1903
1882
|
code,
|
|
1904
1883
|
category: "general",
|
|
1905
1884
|
explanation: "No specialized diagnostic route matched; use LSP and indexed evidence before editing.",
|
|
1906
|
-
suggestedTools: ["get_diagnostics", "explore_pdx_project", "query_cwt_schema", "
|
|
1885
|
+
suggestedTools: ["get_diagnostics", "explore_pdx_project", "query_cwt_schema", "go_to_definition", "query_workspace_index"],
|
|
1907
1886
|
nextSteps: [
|
|
1908
1887
|
"Group similar diagnostics and inspect a representative block.",
|
|
1909
1888
|
"Verify identifiers through indexed tools before making changes."
|
|
@@ -2015,9 +1994,10 @@ var require_rules = __commonJS({
|
|
|
2015
1994
|
async function queryRulesWithHost(host, args) {
|
|
2016
1995
|
const { cache, meta } = await loadCwtRulesMemoized(host);
|
|
2017
1996
|
let rules = args.category === "trigger" ? cache.triggers : args.category === "effect" ? cache.effects : args.category === "modifier" ? cache.modifiers : cache.scopeChanges;
|
|
1997
|
+
const dottedScopeChain = args.name && args.category === "scope_change" ? buildDottedScopeChainRule(args.name, args.scope, cache.scopeChanges) : void 0;
|
|
2018
1998
|
if (args.name) {
|
|
2019
|
-
const needle = args.name.
|
|
2020
|
-
const filtered = rules.filter((rule) => rule.name.toLowerCase().includes(needle));
|
|
1999
|
+
const needle = normalizeRuleNameQuery(args.name, args.category);
|
|
2000
|
+
const filtered = rules.filter((rule) => rule.name.toLowerCase().includes(needle)).sort((a, b) => scoreRuleNameMatch(a.name, needle) - scoreRuleNameMatch(b.name, needle) || a.name.localeCompare(b.name));
|
|
2021
2001
|
if (filtered.length === 0 && rules.length > 0) {
|
|
2022
2002
|
rules = rules.map((rule) => ({ rule, score: levenshtein(needle, rule.name.toLowerCase()) })).sort((a, b) => a.score - b.score).slice(0, 5).map((item) => ({
|
|
2023
2003
|
...item.rule,
|
|
@@ -2027,6 +2007,12 @@ var require_rules = __commonJS({
|
|
|
2027
2007
|
rules = filtered;
|
|
2028
2008
|
}
|
|
2029
2009
|
}
|
|
2010
|
+
if (dottedScopeChain) {
|
|
2011
|
+
rules = [
|
|
2012
|
+
dottedScopeChain,
|
|
2013
|
+
...rules.filter((rule) => rule.name !== dottedScopeChain.name)
|
|
2014
|
+
];
|
|
2015
|
+
}
|
|
2030
2016
|
if (args.scope) {
|
|
2031
2017
|
const scope = args.scope.toLowerCase();
|
|
2032
2018
|
rules = rules.filter((rule) => rule.scopes.length === 0 || rule.scopes.some((candidate) => {
|
|
@@ -2264,13 +2250,41 @@ var require_rules = __commonJS({
|
|
|
2264
2250
|
}
|
|
2265
2251
|
async function findCwtSchemaFiles(host, root, maxFiles) {
|
|
2266
2252
|
if (host.rules?.listCwtFiles) {
|
|
2267
|
-
return (await host.rules.listCwtFiles(root, { limit: maxFiles })).slice(0, maxFiles);
|
|
2253
|
+
return (await host.rules.listCwtFiles(root, { limit: maxFiles })).slice(0, maxFiles).sort((a, b) => a.localeCompare(b));
|
|
2254
|
+
}
|
|
2255
|
+
if (fs.existsSync(root)) {
|
|
2256
|
+
const results2 = [];
|
|
2257
|
+
const ignoredDirs2 = /* @__PURE__ */ new Set([".git", "node_modules", "logs", "config"]);
|
|
2258
|
+
const walkDisk = (dir, depth) => {
|
|
2259
|
+
if (results2.length >= maxFiles || depth > 8)
|
|
2260
|
+
return;
|
|
2261
|
+
let entries;
|
|
2262
|
+
try {
|
|
2263
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
2264
|
+
} catch {
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
2268
|
+
for (const entry of entries) {
|
|
2269
|
+
if (results2.length >= maxFiles)
|
|
2270
|
+
break;
|
|
2271
|
+
const fullPath = path.join(dir, entry.name);
|
|
2272
|
+
if (entry.isDirectory()) {
|
|
2273
|
+
if (!ignoredDirs2.has(entry.name))
|
|
2274
|
+
walkDisk(fullPath, depth + 1);
|
|
2275
|
+
} else if (entry.isFile() && entry.name.toLowerCase().endsWith(".cwt")) {
|
|
2276
|
+
results2.push(fullPath);
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
};
|
|
2280
|
+
walkDisk(root, 0);
|
|
2281
|
+
return results2.sort((a, b) => a.localeCompare(b));
|
|
2268
2282
|
}
|
|
2269
2283
|
const rootRelative = workspaceRelativePath(host.workspaceRoot, root);
|
|
2270
2284
|
if (!rootRelative)
|
|
2271
2285
|
return [];
|
|
2272
2286
|
const results = [];
|
|
2273
|
-
const ignoredDirs = /* @__PURE__ */ new Set([".git", "node_modules", "logs"]);
|
|
2287
|
+
const ignoredDirs = /* @__PURE__ */ new Set([".git", "node_modules", "logs", "config"]);
|
|
2274
2288
|
const walk = async (relativeDir, depth) => {
|
|
2275
2289
|
if (results.length >= maxFiles || depth > 8)
|
|
2276
2290
|
return;
|
|
@@ -2280,6 +2294,7 @@ var require_rules = __commonJS({
|
|
|
2280
2294
|
} catch {
|
|
2281
2295
|
return;
|
|
2282
2296
|
}
|
|
2297
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
2283
2298
|
for (const entry of entries) {
|
|
2284
2299
|
if (results.length >= maxFiles)
|
|
2285
2300
|
break;
|
|
@@ -2294,7 +2309,16 @@ var require_rules = __commonJS({
|
|
|
2294
2309
|
}
|
|
2295
2310
|
};
|
|
2296
2311
|
await walk(rootRelative, 0);
|
|
2297
|
-
return results;
|
|
2312
|
+
return results.sort((a, b) => a.localeCompare(b));
|
|
2313
|
+
}
|
|
2314
|
+
async function collectCwtRuleSourceFiles(host, root, includeMissingLogs) {
|
|
2315
|
+
const files = await findCwtSchemaFiles(host, root, CWT_RULE_FILE_SCAN_LIMIT);
|
|
2316
|
+
for (const relativeLog of CWT_RULE_LOG_CANDIDATES) {
|
|
2317
|
+
const fullPath = path.join(root, relativeLog);
|
|
2318
|
+
if (includeMissingLogs || fs.existsSync(fullPath))
|
|
2319
|
+
files.push(fullPath);
|
|
2320
|
+
}
|
|
2321
|
+
return Array.from(new Set(files)).sort((a, b) => a.localeCompare(b));
|
|
2298
2322
|
}
|
|
2299
2323
|
function workspaceRelativePath(workspaceRoot, fullPath) {
|
|
2300
2324
|
const relative = path.relative(workspaceRoot, fullPath);
|
|
@@ -2627,29 +2651,19 @@ var require_rules = __commonJS({
|
|
|
2627
2651
|
score += 20;
|
|
2628
2652
|
return score;
|
|
2629
2653
|
}
|
|
2630
|
-
var
|
|
2631
|
-
"scopes.cwt",
|
|
2654
|
+
var CWT_RULE_LOG_CANDIDATES = [
|
|
2632
2655
|
path.join("logs", "trigger_docs.log"),
|
|
2633
|
-
path.join("logs", "modifiers.log")
|
|
2634
|
-
"triggers.cwt",
|
|
2635
|
-
"trigger.cwt",
|
|
2636
|
-
path.join("generated", "triggers.generated.cwt"),
|
|
2637
|
-
"effects.cwt",
|
|
2638
|
-
"effect.cwt",
|
|
2639
|
-
path.join("generated", "effects.generated.cwt"),
|
|
2640
|
-
"modifier.cwt",
|
|
2641
|
-
"scope_changes.cwt",
|
|
2642
|
-
path.join("generated", "scope_changes.generated.cwt")
|
|
2656
|
+
path.join("logs", "modifiers.log")
|
|
2643
2657
|
];
|
|
2658
|
+
var CWT_RULE_FILE_SCAN_LIMIT = 5e3;
|
|
2644
2659
|
var CWT_RULES_MEMO_MAX_ENTRIES = 8;
|
|
2645
2660
|
var CWT_RULES_MEMO_REFRESH_MS = 3e4;
|
|
2646
2661
|
var cwtRulesMemo = /* @__PURE__ */ new Map();
|
|
2647
|
-
function computeRulesSignature(configPaths) {
|
|
2662
|
+
async function computeRulesSignature(host, configPaths) {
|
|
2648
2663
|
const parts = [];
|
|
2649
2664
|
let sawDiskFiles = false;
|
|
2650
2665
|
for (const configPath of configPaths) {
|
|
2651
|
-
for (const
|
|
2652
|
-
const fullPath = path.join(configPath, file);
|
|
2666
|
+
for (const fullPath of await collectCwtRuleSourceFiles(host, configPath, true)) {
|
|
2653
2667
|
try {
|
|
2654
2668
|
const stat = fs.statSync(fullPath);
|
|
2655
2669
|
parts.push(`${fullPath}:${stat.mtimeMs}:${stat.size}`);
|
|
@@ -2664,8 +2678,8 @@ var require_rules = __commonJS({
|
|
|
2664
2678
|
async function computeRulesContentHash(host, configPaths) {
|
|
2665
2679
|
const hash = crypto2.createHash("sha256");
|
|
2666
2680
|
for (const configPath of configPaths) {
|
|
2667
|
-
for (const file of
|
|
2668
|
-
const read = await readRulesTextFile(host,
|
|
2681
|
+
for (const file of await collectCwtRuleSourceFiles(host, configPath, true)) {
|
|
2682
|
+
const read = await readRulesTextFile(host, file).catch(() => ({ exists: false, content: "", hasBom: false }));
|
|
2669
2683
|
if (!read.exists)
|
|
2670
2684
|
continue;
|
|
2671
2685
|
hash.update(`${read.content.length}:`);
|
|
@@ -2680,7 +2694,7 @@ var require_rules = __commonJS({
|
|
|
2680
2694
|
async function loadCwtRulesMemoized(host) {
|
|
2681
2695
|
const configPaths = await resolveRulesConfigPaths(host);
|
|
2682
2696
|
const hostKey = cwtRulesHostKey(host);
|
|
2683
|
-
const { signature, sawDiskFiles } = computeRulesSignature(configPaths);
|
|
2697
|
+
const { signature, sawDiskFiles } = await computeRulesSignature(host, configPaths);
|
|
2684
2698
|
const memo = cwtRulesMemo.get(hostKey);
|
|
2685
2699
|
if (memo && memo.signature === signature && (memo.sawDiskFiles || host.now() - memo.computedAt < CWT_RULES_MEMO_REFRESH_MS)) {
|
|
2686
2700
|
return { cache: memo.cache, meta: { generation: memo.generation, contentHash: memo.contentHash } };
|
|
@@ -2711,10 +2725,25 @@ var require_rules = __commonJS({
|
|
|
2711
2725
|
const docs = triggerDocs.exists ? parseDocsLog(triggerDocs.content, path.join(configPath, "logs", "trigger_docs.log")) : /* @__PURE__ */ new Map();
|
|
2712
2726
|
const scopesRead = await readRulesTextFile(host, path.join(configPath, "scopes.cwt")).catch(() => ({ exists: false, content: "", hasBom: false }));
|
|
2713
2727
|
const scopes = scopesRead.exists ? parseScopesFile(scopesRead.content, path.join(configPath, "scopes.cwt")) : /* @__PURE__ */ new Map();
|
|
2714
|
-
const triggers =
|
|
2715
|
-
const effects =
|
|
2716
|
-
const scopeChanges =
|
|
2717
|
-
const modifierAliases =
|
|
2728
|
+
const triggers = [];
|
|
2729
|
+
const effects = [];
|
|
2730
|
+
const scopeChanges = [];
|
|
2731
|
+
const modifierAliases = [];
|
|
2732
|
+
for (const file of await findCwtSchemaFiles(host, configPath, CWT_RULE_FILE_SCAN_LIMIT)) {
|
|
2733
|
+
const relativeFile = path.relative(configPath, file).replace(/\\/g, "/");
|
|
2734
|
+
const parsed = await readRulesFile(host, file, scopeChangeFileCategoryOverride(relativeFile), docs, scopes);
|
|
2735
|
+
for (const rule of parsed) {
|
|
2736
|
+
if (rule.category === "trigger")
|
|
2737
|
+
triggers.push(rule);
|
|
2738
|
+
else if (rule.category === "effect")
|
|
2739
|
+
effects.push(rule);
|
|
2740
|
+
else if (rule.category === "modifier")
|
|
2741
|
+
modifierAliases.push(rule);
|
|
2742
|
+
else
|
|
2743
|
+
scopeChanges.push(rule);
|
|
2744
|
+
}
|
|
2745
|
+
scopeChanges.push(...await readLinksFile(host, file, scopes));
|
|
2746
|
+
}
|
|
2718
2747
|
const modifierLog = await readModifiersLog(host, path.join(configPath, "logs", "modifiers.log"));
|
|
2719
2748
|
const modifiers = [...modifierAliases];
|
|
2720
2749
|
const modifierNames = new Set(modifiers.map((rule) => rule.name.toLowerCase()));
|
|
@@ -2768,12 +2797,8 @@ var require_rules = __commonJS({
|
|
|
2768
2797
|
return normalizeGameId(game.id);
|
|
2769
2798
|
}
|
|
2770
2799
|
async function readProjectProfileFile(host) {
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
if (!read.exists) {
|
|
2774
|
-
const legacyPath = path.join(host.workspaceRoot, ".cwtools-ai", "project", "profile.json");
|
|
2775
|
-
read = await host.filesystem.readTextFile(legacyPath).catch(() => ({ exists: false, content: "", hasBom: false }));
|
|
2776
|
-
}
|
|
2800
|
+
const profilePath = path.join(host.workspaceRoot, ".cwtools", "project", "profile.json");
|
|
2801
|
+
const read = await host.filesystem.readTextFile(profilePath).catch(() => ({ exists: false, content: "", hasBom: false }));
|
|
2777
2802
|
if (!read.exists)
|
|
2778
2803
|
return null;
|
|
2779
2804
|
try {
|
|
@@ -2798,19 +2823,16 @@ var require_rules = __commonJS({
|
|
|
2798
2823
|
return host.rules.readTextFile(filePath);
|
|
2799
2824
|
return host.filesystem.readTextFile(filePath);
|
|
2800
2825
|
}
|
|
2801
|
-
async function readRuleFiles(host, configPath, relativeFiles, category, docs, scopes) {
|
|
2802
|
-
const rules = [];
|
|
2803
|
-
for (const relativeFile of relativeFiles) {
|
|
2804
|
-
rules.push(...await readRulesFile(host, path.join(configPath, relativeFile), category, docs, scopes));
|
|
2805
|
-
}
|
|
2806
|
-
return rules;
|
|
2807
|
-
}
|
|
2808
2826
|
async function readRulesFile(host, filePath, category, docs, scopes) {
|
|
2809
2827
|
const read = await readRulesTextFile(host, filePath).catch(() => ({ exists: false, content: "", hasBom: false }));
|
|
2810
2828
|
if (!read.exists)
|
|
2811
2829
|
return [];
|
|
2812
2830
|
return parseCwtFile(read.content, filePath, category, docs, scopes);
|
|
2813
2831
|
}
|
|
2832
|
+
function scopeChangeFileCategoryOverride(relativeRuleFile) {
|
|
2833
|
+
const base = path.posix.basename(relativeRuleFile.replace(/\\/g, "/")).toLowerCase();
|
|
2834
|
+
return base === "scope_changes.cwt" || base === "scope_changes.generated.cwt" || base === "scope_change.cwt" ? "scope_change" : void 0;
|
|
2835
|
+
}
|
|
2814
2836
|
async function readModifiersLog(host, filePath) {
|
|
2815
2837
|
const read = await readRulesTextFile(host, filePath).catch(() => ({ exists: false, content: "", hasBom: false }));
|
|
2816
2838
|
if (!read.exists)
|
|
@@ -2921,7 +2943,7 @@ var require_rules = __commonJS({
|
|
|
2921
2943
|
}
|
|
2922
2944
|
return scopes;
|
|
2923
2945
|
}
|
|
2924
|
-
function parseCwtFile(content, filePath,
|
|
2946
|
+
function parseCwtFile(content, filePath, categoryOverride, docs, scopes) {
|
|
2925
2947
|
const results = [];
|
|
2926
2948
|
let currentScopes = [];
|
|
2927
2949
|
let currentSupportedScopes = [];
|
|
@@ -2966,13 +2988,15 @@ var require_rules = __commonJS({
|
|
|
2966
2988
|
currentDesc = comment;
|
|
2967
2989
|
continue;
|
|
2968
2990
|
}
|
|
2969
|
-
const nameMatch = line.match(/^alias\[(
|
|
2970
|
-
if (nameMatch?.[1]) {
|
|
2971
|
-
const
|
|
2991
|
+
const nameMatch = line.match(/^alias\[(trigger|effect|modifier):([^\]]+)\]\s*=\s*(.*)/);
|
|
2992
|
+
if (nameMatch?.[1] && nameMatch[2]) {
|
|
2993
|
+
const aliasKind = nameMatch[1];
|
|
2994
|
+
const category = categoryOverride ?? aliasKind;
|
|
2995
|
+
const name = nameMatch[2];
|
|
2972
2996
|
const doc = docs.get(name);
|
|
2973
2997
|
const cwtBlockText = collectCwtBlockText(lines, i);
|
|
2974
2998
|
const scopesForRule = doc?.scopes.length ? doc.scopes : currentSupportedScopes.length ? currentSupportedScopes : currentScopes;
|
|
2975
|
-
const syntax = doc?.syntax || normalizeInlineSyntax(name, nameMatch[
|
|
2999
|
+
const syntax = doc?.syntax || normalizeInlineSyntax(name, nameMatch[3] ?? "");
|
|
2976
3000
|
const description = doc?.description || currentDesc;
|
|
2977
3001
|
const semanticHints = buildSemanticHints({
|
|
2978
3002
|
description,
|
|
@@ -3016,6 +3040,91 @@ var require_rules = __commonJS({
|
|
|
3016
3040
|
}
|
|
3017
3041
|
return results;
|
|
3018
3042
|
}
|
|
3043
|
+
async function readLinksFile(host, filePath, scopes) {
|
|
3044
|
+
const read = await readRulesTextFile(host, filePath).catch(() => ({ exists: false, content: "", hasBom: false }));
|
|
3045
|
+
if (!read.exists || !/^\s*links\s*=\s*\{/im.test(read.content))
|
|
3046
|
+
return [];
|
|
3047
|
+
return parseLinksCwtFile(read.content, filePath, scopes);
|
|
3048
|
+
}
|
|
3049
|
+
function parseLinksCwtFile(content, filePath, scopes) {
|
|
3050
|
+
const results = [];
|
|
3051
|
+
const lines = content.split(/\r?\n/);
|
|
3052
|
+
let inLinks = false;
|
|
3053
|
+
let depth = 0;
|
|
3054
|
+
let current;
|
|
3055
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3056
|
+
const rawLine = lines[i] ?? "";
|
|
3057
|
+
const line = stripCwtLineComment(rawLine).trim();
|
|
3058
|
+
if (!inLinks) {
|
|
3059
|
+
if (/^links\s*=\s*\{/.test(line)) {
|
|
3060
|
+
inLinks = true;
|
|
3061
|
+
depth = countBraceDelta(line);
|
|
3062
|
+
}
|
|
3063
|
+
continue;
|
|
3064
|
+
}
|
|
3065
|
+
if (!current && depth === 1) {
|
|
3066
|
+
const linkMatch = line.match(/^([A-Za-z_][\w.-]*)\s*=\s*\{\s*$/);
|
|
3067
|
+
if (linkMatch?.[1])
|
|
3068
|
+
current = { name: linkMatch[1], line: i + 1, inputScopes: [] };
|
|
3069
|
+
} else if (current) {
|
|
3070
|
+
const inputMatch = line.match(/^input_scopes\s*=\s*(.*)$/i);
|
|
3071
|
+
if (inputMatch?.[1])
|
|
3072
|
+
current.inputScopes = splitRuleValueList(inputMatch[1]).map(normalizeScopeName);
|
|
3073
|
+
const outputMatch = line.match(/^output_scope\s*=\s*(.*)$/i);
|
|
3074
|
+
if (outputMatch?.[1]) {
|
|
3075
|
+
const outputScope = stripRuleValueBraces(outputMatch[1]).split(/\s+/)[0];
|
|
3076
|
+
if (outputScope)
|
|
3077
|
+
current.outputScope = normalizeScopeName(outputScope);
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
depth += countBraceDelta(line);
|
|
3081
|
+
if (current && depth <= 1) {
|
|
3082
|
+
if (current.outputScope) {
|
|
3083
|
+
const inputScopes = current.inputScopes.length ? current.inputScopes : ["all"];
|
|
3084
|
+
const syntax = `${current.name} = scope link (${inputScopes.join(" | ")} -> ${current.outputScope})`;
|
|
3085
|
+
const linkHint = {
|
|
3086
|
+
text: `Legal scope link '${current.name}' accepts input scopes { ${inputScopes.join(" ")} } and outputs '${current.outputScope}'. Context pointers such as from/prev/root/this select the current input scope; they are not fixed object fields.`,
|
|
3087
|
+
source: "links.cwt",
|
|
3088
|
+
file: filePath,
|
|
3089
|
+
line: current.line,
|
|
3090
|
+
confidence: "hint"
|
|
3091
|
+
};
|
|
3092
|
+
const scopeHints = buildSemanticHints({
|
|
3093
|
+
description: "",
|
|
3094
|
+
cwtDescription: "",
|
|
3095
|
+
scopes,
|
|
3096
|
+
relatedScopeNames: [...inputScopes, current.outputScope],
|
|
3097
|
+
cwtFile: filePath,
|
|
3098
|
+
cwtLine: current.line
|
|
3099
|
+
});
|
|
3100
|
+
results.push({
|
|
3101
|
+
name: current.name,
|
|
3102
|
+
description: `Legal scope link from { ${inputScopes.join(" ")} } to ${current.outputScope}.`,
|
|
3103
|
+
scopes: inputScopes,
|
|
3104
|
+
syntax,
|
|
3105
|
+
category: "scope_change",
|
|
3106
|
+
sourceFile: filePath,
|
|
3107
|
+
sourceLine: current.line,
|
|
3108
|
+
hardFacts: {
|
|
3109
|
+
category: "scope_change",
|
|
3110
|
+
supportedScopes: inputScopes,
|
|
3111
|
+
pushScope: current.outputScope,
|
|
3112
|
+
valueReferences: [],
|
|
3113
|
+
syntax,
|
|
3114
|
+
cwtSource: { file: filePath, line: current.line }
|
|
3115
|
+
},
|
|
3116
|
+
semanticHints: [linkHint, ...scopeHints].slice(0, 8)
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
current = void 0;
|
|
3120
|
+
}
|
|
3121
|
+
if (inLinks && depth <= 0) {
|
|
3122
|
+
inLinks = false;
|
|
3123
|
+
current = void 0;
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
return results;
|
|
3127
|
+
}
|
|
3019
3128
|
function buildSemanticHints(args) {
|
|
3020
3129
|
const hints = [];
|
|
3021
3130
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -3157,7 +3266,12 @@ var require_rules = __commonJS({
|
|
|
3157
3266
|
[/触发器|觸發器/g, ["trigger"]],
|
|
3158
3267
|
[/效果|效应|效應/g, ["effect"]]
|
|
3159
3268
|
];
|
|
3160
|
-
const expanded = [
|
|
3269
|
+
const expanded = [];
|
|
3270
|
+
for (const token of direct) {
|
|
3271
|
+
expanded.push(token);
|
|
3272
|
+
if (/[.:]/.test(token))
|
|
3273
|
+
expanded.push(...token.split(/[.:]+/).filter(Boolean));
|
|
3274
|
+
}
|
|
3161
3275
|
for (const [pattern, tokens] of synonyms) {
|
|
3162
3276
|
pattern.lastIndex = 0;
|
|
3163
3277
|
if (pattern.test(intent))
|
|
@@ -3233,6 +3347,94 @@ var require_rules = __commonJS({
|
|
|
3233
3347
|
function stripRuleValueBraces(value) {
|
|
3234
3348
|
return value.replace(/^\{\s*/, "").replace(/\s*\}$/, "").trim();
|
|
3235
3349
|
}
|
|
3350
|
+
function normalizeRuleNameQuery(name, category) {
|
|
3351
|
+
const lowered = name.trim().toLowerCase();
|
|
3352
|
+
if (category !== "scope_change" || !lowered.includes("."))
|
|
3353
|
+
return lowered;
|
|
3354
|
+
const parts = lowered.split(".").map((part) => part.trim()).filter(Boolean);
|
|
3355
|
+
return parts[parts.length - 1] ?? lowered;
|
|
3356
|
+
}
|
|
3357
|
+
function buildDottedScopeChainRule(query, contextScope, scopeChanges) {
|
|
3358
|
+
const rawParts = query.trim().split(".").map(normalizeScopeName).filter(Boolean);
|
|
3359
|
+
if (rawParts.length < 2)
|
|
3360
|
+
return void 0;
|
|
3361
|
+
const contextPointers = /* @__PURE__ */ new Set(["from", "prev", "root", "this"]);
|
|
3362
|
+
const first = rawParts[0];
|
|
3363
|
+
const startsWithPointer = contextPointers.has(first);
|
|
3364
|
+
const initialScope = startsWithPointer ? contextScope?.trim().toLowerCase() : first;
|
|
3365
|
+
const linkNames = rawParts.slice(1);
|
|
3366
|
+
if (!initialScope || linkNames.length === 0)
|
|
3367
|
+
return void 0;
|
|
3368
|
+
const hops = [];
|
|
3369
|
+
let currentScope = initialScope;
|
|
3370
|
+
for (const linkName of linkNames) {
|
|
3371
|
+
const link = scopeChanges.find((rule) => rule.name.toLowerCase() === linkName && !!rule.hardFacts?.pushScope && scopeListContains(rule.hardFacts.supportedScopes ?? rule.scopes, currentScope));
|
|
3372
|
+
const outputScope = link?.hardFacts?.pushScope?.trim().toLowerCase();
|
|
3373
|
+
if (!link || !outputScope)
|
|
3374
|
+
return void 0;
|
|
3375
|
+
hops.push({ link, inputScope: currentScope, outputScope });
|
|
3376
|
+
currentScope = outputScope;
|
|
3377
|
+
}
|
|
3378
|
+
if (hops.length === 0)
|
|
3379
|
+
return void 0;
|
|
3380
|
+
const normalizedQuery = rawParts.join(".");
|
|
3381
|
+
const syntax = `${normalizedQuery} = { ... }`;
|
|
3382
|
+
const source = hops[0].link;
|
|
3383
|
+
const hopText = hops.map((hop) => `${hop.inputScope}.${hop.link.name} -> ${hop.outputScope}`).join("; ");
|
|
3384
|
+
const linkHints = hops.map((hop) => ({
|
|
3385
|
+
text: `Dotted scope chain hop '${hop.inputScope}.${hop.link.name}' is legal because links.cwt declares '${hop.link.name}' with input scope '${hop.inputScope}' and output scope '${hop.outputScope}'.`,
|
|
3386
|
+
source: "links.cwt",
|
|
3387
|
+
file: hop.link.sourceFile,
|
|
3388
|
+
line: hop.link.sourceLine,
|
|
3389
|
+
confidence: "hint"
|
|
3390
|
+
}));
|
|
3391
|
+
return {
|
|
3392
|
+
name: normalizedQuery,
|
|
3393
|
+
description: `Legal dotted scope chain from ${initialScope} to ${currentScope}. Hops: ${hopText}.`,
|
|
3394
|
+
scopes: [initialScope],
|
|
3395
|
+
syntax,
|
|
3396
|
+
category: "scope_change",
|
|
3397
|
+
sourceFile: source.sourceFile,
|
|
3398
|
+
sourceLine: source.sourceLine,
|
|
3399
|
+
hardFacts: {
|
|
3400
|
+
category: "scope_change",
|
|
3401
|
+
supportedScopes: [initialScope],
|
|
3402
|
+
pushScope: currentScope,
|
|
3403
|
+
valueReferences: [],
|
|
3404
|
+
syntax,
|
|
3405
|
+
cwtSource: source.sourceFile && source.sourceLine ? { file: source.sourceFile, line: source.sourceLine } : void 0
|
|
3406
|
+
},
|
|
3407
|
+
semanticHints: linkHints.slice(0, 8)
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3410
|
+
function scopeListContains(scopes, scope) {
|
|
3411
|
+
const lowerScope = scope.toLowerCase();
|
|
3412
|
+
return scopes.some((candidate) => {
|
|
3413
|
+
const lower = candidate.toLowerCase();
|
|
3414
|
+
return lower === lowerScope || lower === "all" || lower === "any";
|
|
3415
|
+
});
|
|
3416
|
+
}
|
|
3417
|
+
function scoreRuleNameMatch(name, needle) {
|
|
3418
|
+
const lower = name.toLowerCase();
|
|
3419
|
+
if (lower === needle)
|
|
3420
|
+
return 0;
|
|
3421
|
+
if (lower.startsWith(needle))
|
|
3422
|
+
return 1;
|
|
3423
|
+
return 2;
|
|
3424
|
+
}
|
|
3425
|
+
function normalizeScopeName(scope) {
|
|
3426
|
+
return scope.replace(/^["']|["']$/g, "").trim().toLowerCase();
|
|
3427
|
+
}
|
|
3428
|
+
function countBraceDelta(line) {
|
|
3429
|
+
let delta = 0;
|
|
3430
|
+
for (const ch of line) {
|
|
3431
|
+
if (ch === "{")
|
|
3432
|
+
delta += 1;
|
|
3433
|
+
else if (ch === "}")
|
|
3434
|
+
delta -= 1;
|
|
3435
|
+
}
|
|
3436
|
+
return delta;
|
|
3437
|
+
}
|
|
3236
3438
|
function splitWords(value) {
|
|
3237
3439
|
return value.split(/\s+/).map((part) => part.trim()).filter(Boolean);
|
|
3238
3440
|
}
|
|
@@ -3280,7 +3482,7 @@ var require_workflowHints = __commonJS({
|
|
|
3280
3482
|
id: "entity-lookup",
|
|
3281
3483
|
title: "Verify game identifiers",
|
|
3282
3484
|
triggers: ["identifier", "reference", "definition", "entity"],
|
|
3283
|
-
recommendedTools: ["query_project_knowledge", "explore_pdx_project", "query_cwt_schema", "search_rule_capabilities", "query_rules", "query_types", "query_workspace_index", "
|
|
3485
|
+
recommendedTools: ["query_project_knowledge", "explore_pdx_project", "query_cwt_schema", "search_rule_capabilities", "query_rules", "query_types", "query_workspace_index", "go_to_definition"],
|
|
3284
3486
|
guardrails: ["Treat empty text search results as inconclusive without indexed verification.", "Treat semanticHints as retrieval hints, not legality proof."]
|
|
3285
3487
|
}
|
|
3286
3488
|
]
|
|
@@ -3335,27 +3537,16 @@ var require_profile = __commonJS({
|
|
|
3335
3537
|
exports2.getProjectProfilePath = getProjectProfilePath;
|
|
3336
3538
|
exports2.isProjectProfile = isProjectProfile;
|
|
3337
3539
|
exports2.buildProfileSummary = buildProfileSummary;
|
|
3338
|
-
exports2.
|
|
3540
|
+
exports2.getGuidanceCard = getGuidanceCard;
|
|
3339
3541
|
exports2.selectProfileSection = selectProfileSection;
|
|
3340
3542
|
exports2.queryProjectProfileWithHost = queryProjectProfileWithHost;
|
|
3341
3543
|
var path = __importStar(require("path"));
|
|
3342
3544
|
exports2.PROJECT_PROFILE_RELATIVE_PATH = path.join(".cwtools", "project", "profile.json");
|
|
3343
3545
|
function getProjectProfilePath(workspaceRoot) {
|
|
3344
|
-
|
|
3345
|
-
const fs = require("fs");
|
|
3346
|
-
const primary = path.join(workspaceRoot, ".cwtools", "project", "profile.json");
|
|
3347
|
-
if (fs.existsSync(primary))
|
|
3348
|
-
return primary;
|
|
3349
|
-
const legacy = path.join(workspaceRoot, ".cwtools-ai", "project", "profile.json");
|
|
3350
|
-
if (fs.existsSync(legacy))
|
|
3351
|
-
return legacy;
|
|
3352
|
-
return primary;
|
|
3353
|
-
} catch {
|
|
3354
|
-
return path.join(workspaceRoot, exports2.PROJECT_PROFILE_RELATIVE_PATH);
|
|
3355
|
-
}
|
|
3546
|
+
return path.join(workspaceRoot, exports2.PROJECT_PROFILE_RELATIVE_PATH);
|
|
3356
3547
|
}
|
|
3357
3548
|
function isProjectProfile(value) {
|
|
3358
|
-
return !!value && typeof value === "object" &&
|
|
3549
|
+
return !!value && typeof value === "object" && value.schemaVersion === 4 && typeof value.projectName === "string";
|
|
3359
3550
|
}
|
|
3360
3551
|
function buildProfileSummary(profile) {
|
|
3361
3552
|
const dirs = profile.keyDirectories.filter((dir) => dir.exists).map((dir) => dir.path).slice(0, 8).join(", ") || "none";
|
|
@@ -3371,16 +3562,10 @@ var require_profile = __commonJS({
|
|
|
3371
3562
|
`Localisation: ${languages} (${profile.localisation.encoding})`
|
|
3372
3563
|
].join("\n");
|
|
3373
3564
|
}
|
|
3374
|
-
function
|
|
3375
|
-
if (!
|
|
3565
|
+
function getGuidanceCard(profile, guidance) {
|
|
3566
|
+
if (!guidance)
|
|
3376
3567
|
return void 0;
|
|
3377
|
-
|
|
3378
|
-
return profile.promptCards.loc_writer ?? profile.promptCards.build;
|
|
3379
|
-
if (mode === "gui_expert")
|
|
3380
|
-
return profile.promptCards.asset ?? profile.promptCards.build;
|
|
3381
|
-
if (mode === "script_reviewer")
|
|
3382
|
-
return profile.promptCards.review;
|
|
3383
|
-
return profile.promptCards[mode] ?? profile.promptCards.build;
|
|
3568
|
+
return profile.guidanceCards[guidance] ?? profile.guidanceCards.implementation;
|
|
3384
3569
|
}
|
|
3385
3570
|
function selectProfileSection(profile, section) {
|
|
3386
3571
|
switch (section) {
|
|
@@ -3402,8 +3587,8 @@ var require_profile = __commonJS({
|
|
|
3402
3587
|
vanillaCache: profile.validation?.vanillaCache,
|
|
3403
3588
|
game: profile.game
|
|
3404
3589
|
};
|
|
3405
|
-
case "
|
|
3406
|
-
return profile.
|
|
3590
|
+
case "guidanceCards":
|
|
3591
|
+
return profile.guidanceCards;
|
|
3407
3592
|
case "all":
|
|
3408
3593
|
return profile;
|
|
3409
3594
|
case "summary":
|
|
@@ -3448,7 +3633,7 @@ var require_profile = __commonJS({
|
|
|
3448
3633
|
source: "cwtools-shared",
|
|
3449
3634
|
error: {
|
|
3450
3635
|
code: "invalid_profile",
|
|
3451
|
-
message: "Project profile exists but is not a valid schemaVersion
|
|
3636
|
+
message: "Project profile exists but is not a valid schemaVersion 4 profile."
|
|
3452
3637
|
}
|
|
3453
3638
|
};
|
|
3454
3639
|
}
|
|
@@ -3465,8 +3650,8 @@ var require_profile = __commonJS({
|
|
|
3465
3650
|
profile: section === "all" ? parsed : void 0,
|
|
3466
3651
|
summary: buildProfileSummary(parsed),
|
|
3467
3652
|
data: selectProfileSection(parsed, section),
|
|
3468
|
-
|
|
3469
|
-
_hint: 'Use section="routing", "localisation", "identifiers", or a
|
|
3653
|
+
guidanceCard: getGuidanceCard(parsed, args.guidance),
|
|
3654
|
+
_hint: 'Use section="routing", "localisation", "identifiers", or a targeted guidance card for focused context.'
|
|
3470
3655
|
}
|
|
3471
3656
|
};
|
|
3472
3657
|
} catch (error) {
|
|
@@ -3717,7 +3902,7 @@ var require_paths = __commonJS({
|
|
|
3717
3902
|
}
|
|
3718
3903
|
function isScratchRelativePath(relativePath) {
|
|
3719
3904
|
const parts = relativePath.replace(/\\/g, "/").toLowerCase().split("/");
|
|
3720
|
-
return parts.includes(".cwtools")
|
|
3905
|
+
return parts.includes(".cwtools");
|
|
3721
3906
|
}
|
|
3722
3907
|
function validateLocalisationPath(workspaceRoot, inputPath) {
|
|
3723
3908
|
const resolution = resolveWorkspacePath(workspaceRoot, inputPath);
|
|
@@ -3821,9 +4006,8 @@ var require_names = __commonJS({
|
|
|
3821
4006
|
"get_completion_at",
|
|
3822
4007
|
"document_symbols",
|
|
3823
4008
|
"workspace_symbols",
|
|
3824
|
-
"
|
|
3825
|
-
"
|
|
3826
|
-
"query_references",
|
|
4009
|
+
"go_to_definition",
|
|
4010
|
+
"find_references",
|
|
3827
4011
|
"query_scripted_effects",
|
|
3828
4012
|
"query_scripted_triggers",
|
|
3829
4013
|
"query_enums",
|
|
@@ -4203,7 +4387,11 @@ var require_symbols = __commonJS({
|
|
|
4203
4387
|
};
|
|
4204
4388
|
}
|
|
4205
4389
|
async function queryDefinitionWithHost(host, args) {
|
|
4206
|
-
const
|
|
4390
|
+
const file = String(args.file ?? "").trim();
|
|
4391
|
+
if (!file || !Number.isInteger(args.line) || args.line < 0 || !Number.isInteger(args.column) || args.column < 0) {
|
|
4392
|
+
return (0, schema_1.toolDenied)("invalid_arguments", "go_to_definition requires symbolName or a non-negative file position.");
|
|
4393
|
+
}
|
|
4394
|
+
const resolution = resolveReadableFile(host, file);
|
|
4207
4395
|
if ("error" in resolution)
|
|
4208
4396
|
return resolution.error;
|
|
4209
4397
|
const fileUri = toFileUri(resolution.resolvedPath);
|
|
@@ -4232,7 +4420,7 @@ var require_symbols = __commonJS({
|
|
|
4232
4420
|
async function queryDefinitionByNameWithHost(host, args) {
|
|
4233
4421
|
const symbolName = String(args.symbolName ?? "").trim();
|
|
4234
4422
|
if (!symbolName)
|
|
4235
|
-
return (0, schema_1.toolDenied)("invalid_arguments", "
|
|
4423
|
+
return (0, schema_1.toolDenied)("invalid_arguments", "go_to_definition requires symbolName or a file position.");
|
|
4236
4424
|
const commandResult = await host.lsp.executeCommand("cwtools.ai.queryDefinitionByName", [symbolName], { timeoutMs: 1e4 });
|
|
4237
4425
|
if (!isUnavailable(commandResult)) {
|
|
4238
4426
|
return {
|
|
@@ -4258,9 +4446,28 @@ var require_symbols = __commonJS({
|
|
|
4258
4446
|
}
|
|
4259
4447
|
async function queryReferencesWithHost(host, args) {
|
|
4260
4448
|
const identifier = String(args.identifier ?? "").trim();
|
|
4261
|
-
if (!identifier)
|
|
4262
|
-
return (0, schema_1.toolDenied)("invalid_arguments", "query_references requires identifier.");
|
|
4263
4449
|
const limit = clampNumber(args.limit, 50, 1, 200);
|
|
4450
|
+
if (!identifier) {
|
|
4451
|
+
if (!args.file || !Number.isInteger(args.line) || (args.line ?? -1) < 0 || !Number.isInteger(args.column) || (args.column ?? -1) < 0) {
|
|
4452
|
+
return (0, schema_1.toolDenied)("invalid_arguments", "find_references requires identifier or file, line, and column.");
|
|
4453
|
+
}
|
|
4454
|
+
const resolution = resolveReadableFile(host, args.file);
|
|
4455
|
+
if ("error" in resolution)
|
|
4456
|
+
return resolution.error;
|
|
4457
|
+
const requested = await requestLsp(host, "textDocument/references", {
|
|
4458
|
+
textDocument: { uri: toFileUri(resolution.resolvedPath) },
|
|
4459
|
+
position: { line: args.line, character: args.column },
|
|
4460
|
+
context: { includeDeclaration: true }
|
|
4461
|
+
}, 1e4);
|
|
4462
|
+
const references2 = normalizeLocations(requested.data, host.workspaceRoot).slice(0, limit);
|
|
4463
|
+
return {
|
|
4464
|
+
ok: !requested.unavailable,
|
|
4465
|
+
status: requested.unavailable ? "unavailable" : "ready",
|
|
4466
|
+
source: "cwtools-lsp-references",
|
|
4467
|
+
data: { references: references2, total: references2.length },
|
|
4468
|
+
error: requested.unavailable ? { code: "lsp_unavailable", message: requested.message ?? "Reference provider is unavailable." } : void 0
|
|
4469
|
+
};
|
|
4470
|
+
}
|
|
4264
4471
|
const definition = await queryDefinitionByNameWithHost(host, { symbolName: identifier });
|
|
4265
4472
|
const firstMatch = definition.data && typeof definition.data === "object" ? getFirstDefinitionMatch(definition.data) : void 0;
|
|
4266
4473
|
if (firstMatch?.file !== void 0 && firstMatch.line !== void 0 && host.lsp.request) {
|
|
@@ -4482,12 +4689,12 @@ var require_symbols = __commonJS({
|
|
|
4482
4689
|
return "external";
|
|
4483
4690
|
const normalized = filePath.replace(/\\/g, "/").toLowerCase();
|
|
4484
4691
|
if ((0, paths_1.isPathInsideOrEqual)(workspaceRoot, filePath)) {
|
|
4485
|
-
return normalized.includes("/generated/") || normalized.includes("/.cwtools/")
|
|
4692
|
+
return normalized.includes("/generated/") || normalized.includes("/.cwtools/") ? "generated" : "workspace";
|
|
4486
4693
|
}
|
|
4487
4694
|
if (normalized.includes("/vanilla/") || normalized.includes("/cache/") || normalized.includes("/steamapps/common/")) {
|
|
4488
4695
|
return "vanilla";
|
|
4489
4696
|
}
|
|
4490
|
-
if (normalized.includes("/generated/") || normalized.includes("/.cwtools/")
|
|
4697
|
+
if (normalized.includes("/generated/") || normalized.includes("/.cwtools/"))
|
|
4491
4698
|
return "generated";
|
|
4492
4699
|
return "external";
|
|
4493
4700
|
}
|
|
@@ -4977,20 +5184,18 @@ var require_toolHandlers = __commonJS({
|
|
|
4977
5184
|
query: String(args.query ?? ""),
|
|
4978
5185
|
limit: typeof args.limit === "number" ? args.limit : void 0
|
|
4979
5186
|
});
|
|
4980
|
-
case "
|
|
4981
|
-
return (0, symbols_1.queryDefinitionWithHost)(host, {
|
|
5187
|
+
case "go_to_definition":
|
|
5188
|
+
return typeof args.symbolName === "string" && args.symbolName.trim() ? (0, symbols_1.queryDefinitionByNameWithHost)(host, { symbolName: args.symbolName }) : (0, symbols_1.queryDefinitionWithHost)(host, {
|
|
4982
5189
|
file: String(args.file ?? ""),
|
|
4983
5190
|
line: Number(args.line ?? 0),
|
|
4984
5191
|
column: Number(args.column ?? 0)
|
|
4985
5192
|
});
|
|
4986
|
-
case "
|
|
4987
|
-
return (0, symbols_1.queryDefinitionByNameWithHost)(host, {
|
|
4988
|
-
symbolName: String(args.symbolName ?? "")
|
|
4989
|
-
});
|
|
4990
|
-
case "query_references":
|
|
5193
|
+
case "find_references":
|
|
4991
5194
|
return (0, symbols_1.queryReferencesWithHost)(host, {
|
|
4992
|
-
identifier:
|
|
5195
|
+
identifier: typeof args.identifier === "string" ? args.identifier : void 0,
|
|
4993
5196
|
file: typeof args.file === "string" ? args.file : void 0,
|
|
5197
|
+
line: typeof args.line === "number" ? args.line : void 0,
|
|
5198
|
+
column: typeof args.column === "number" ? args.column : void 0,
|
|
4994
5199
|
limit: typeof args.limit === "number" ? args.limit : void 0
|
|
4995
5200
|
});
|
|
4996
5201
|
case "query_scope":
|
|
@@ -10392,7 +10597,6 @@ var require_projectSettings = __commonJS({
|
|
|
10392
10597
|
}
|
|
10393
10598
|
var cache = /* @__PURE__ */ new Map();
|
|
10394
10599
|
var SETTINGS_NAMESPACE = "stellarisLanguageServices";
|
|
10395
|
-
var LEGACY_SETTINGS_NAMESPACE = "cwtools";
|
|
10396
10600
|
function readSettingsObject(workspaceRoot) {
|
|
10397
10601
|
const file = path.join(workspaceRoot, ".vscode", "settings.json");
|
|
10398
10602
|
let mtimeMs;
|
|
@@ -10436,8 +10640,7 @@ var require_projectSettings = __commonJS({
|
|
|
10436
10640
|
const settings = readSettingsObject(workspaceRoot);
|
|
10437
10641
|
if (!settings)
|
|
10438
10642
|
return void 0;
|
|
10439
|
-
|
|
10440
|
-
return current !== void 0 ? current : resolveDotted(settings, `${LEGACY_SETTINGS_NAMESPACE}.${subKey}`);
|
|
10643
|
+
return resolveDotted(settings, `${SETTINGS_NAMESPACE}.${subKey}`);
|
|
10441
10644
|
}
|
|
10442
10645
|
function asNonEmptyStringArray(value) {
|
|
10443
10646
|
return Array.isArray(value) ? value.filter((v) => typeof v === "string" && v.length > 0) : [];
|
|
@@ -10745,7 +10948,7 @@ var require_lspProcessHost = __commonJS({
|
|
|
10745
10948
|
return;
|
|
10746
10949
|
const watcher = (0, chokidar_1.watch)(this.options.workspaceRoot, {
|
|
10747
10950
|
ignoreInitial: true,
|
|
10748
|
-
ignored: [/(^|[\\/])(?:node_modules|\.
|
|
10951
|
+
ignored: [/(^|[\\/])(?:node_modules|\.(?!\.)[^\\/]+)(?:[\\/]|$)/],
|
|
10749
10952
|
awaitWriteFinish: { stabilityThreshold: 120, pollInterval: 30 }
|
|
10750
10953
|
});
|
|
10751
10954
|
watcher.on("add", (filePath) => this.queueWatchedFileChange(filePath, 1));
|
|
@@ -10927,7 +11130,7 @@ var require_lspProcessHost = __commonJS({
|
|
|
10927
11130
|
if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
|
|
10928
11131
|
return false;
|
|
10929
11132
|
const segments = relative.split(/[\\/]+/).map((segment) => segment.toLowerCase());
|
|
10930
|
-
if (segments.some((segment) => segment === "node_modules" || segment
|
|
11133
|
+
if (segments.some((segment) => segment === "node_modules" || segment.startsWith(".") && !segment.startsWith("..")))
|
|
10931
11134
|
return false;
|
|
10932
11135
|
const extension = path.extname(filePath).toLowerCase();
|
|
10933
11136
|
if (extension === ".csv" && game && game.toLowerCase() !== "ck2")
|
|
@@ -10984,7 +11187,7 @@ var require_projectDetect = __commonJS({
|
|
|
10984
11187
|
var path = __importStar(require("path"));
|
|
10985
11188
|
var ROOT_FILE_MARKERS = ["descriptor.mod"];
|
|
10986
11189
|
var ROOT_NESTED_FILE_MARKERS = [path.join(".metadata", "metadata.json")];
|
|
10987
|
-
var ROOT_DIR_MARKERS = [".cwtools"
|
|
11190
|
+
var ROOT_DIR_MARKERS = [".cwtools"];
|
|
10988
11191
|
var CONTENT_DIR_MARKERS = ["common", "events", "localisation", "localization"];
|
|
10989
11192
|
var MAX_ANCESTORS = 6;
|
|
10990
11193
|
var MAX_CHILDREN = 64;
|
|
@@ -11129,7 +11332,7 @@ var require_revalidation = __commonJS({
|
|
|
11129
11332
|
var path = __importStar(require("path"));
|
|
11130
11333
|
var lspProcessHost_1 = require_lspProcessHost();
|
|
11131
11334
|
var REVALIDATE_EXTS = /* @__PURE__ */ new Set([".txt", ".yml", ".gui", ".gfx", ".asset", ".shader", ".fxh"]);
|
|
11132
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"
|
|
11335
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
|
|
11133
11336
|
var MAX_WALK_FILES = 2e4;
|
|
11134
11337
|
var MAX_BATCH = 200;
|
|
11135
11338
|
function numberOr(value, fallback) {
|
|
@@ -11669,7 +11872,7 @@ var require_nodeHostServices = __commonJS({
|
|
|
11669
11872
|
return;
|
|
11670
11873
|
const entries = await fs.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
11671
11874
|
for (const entry of entries) {
|
|
11672
|
-
if (entry.name.startsWith(".") && entry.name !== ".cwtools"
|
|
11875
|
+
if (entry.name.startsWith(".") && entry.name !== ".cwtools")
|
|
11673
11876
|
continue;
|
|
11674
11877
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "coverage")
|
|
11675
11878
|
continue;
|
|
@@ -46774,7 +46977,7 @@ var require_bridgeProxy = __commonJS({
|
|
|
46774
46977
|
"CWTools MCP is served by the active VS Code-compatible extension host.",
|
|
46775
46978
|
"This proxy does not start its own CWTools language server. If calls report",
|
|
46776
46979
|
"bridge_unavailable, open the project in the compatible host where the extension",
|
|
46777
|
-
"is installed and active, or rerun with --standalone to use the
|
|
46980
|
+
"is installed and active, or rerun with --standalone to use the standalone LSP host."
|
|
46778
46981
|
].join("\n")
|
|
46779
46982
|
});
|
|
46780
46983
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
@@ -46863,7 +47066,7 @@ var require_bridgeProxy = __commonJS({
|
|
|
46863
47066
|
throw new Error(`CWTools MCP bridge is unavailable: could not read ${manifestPath}. Open this project in a VS Code-compatible host with the extension active, or pass --standalone to start a separate CWTools server. Details: ${error instanceof Error ? error.message : String(error)}`);
|
|
46864
47067
|
}
|
|
46865
47068
|
if (parsed.kind !== "cwtools-mcp-extension-bridge" || parsed.protocolVersion !== BRIDGE_PROTOCOL_VERSION) {
|
|
46866
|
-
throw new Error(`CWTools MCP bridge manifest at ${manifestPath} is not compatible with this proxy. Open the project in the updated extension host, or pass --standalone
|
|
47069
|
+
throw new Error(`CWTools MCP bridge manifest at ${manifestPath} is not compatible with this proxy. Open the project in the updated extension host, or pass --standalone to use the standalone LSP host.`);
|
|
46867
47070
|
}
|
|
46868
47071
|
if (!parsed.rpcUrl || !parsed.token) {
|
|
46869
47072
|
throw new Error(`CWTools MCP bridge manifest at ${manifestPath} is missing rpcUrl/token.`);
|
|
@@ -49516,7 +49719,7 @@ var require_server3 = __commonJS({
|
|
|
49516
49719
|
"- Before declaring code correct, review get_diagnostics (whole project). Honor the",
|
|
49517
49720
|
" readiness/freshness fields: if readiness.ready is false the project is still loading \u2014",
|
|
49518
49721
|
" retry; an empty result then is not authoritative.",
|
|
49519
|
-
"- Navigate with
|
|
49722
|
+
"- Navigate with go_to_definition / find_references /",
|
|
49520
49723
|
" document_symbols / workspace_symbols; read structured blocks with get_pdx_block",
|
|
49521
49724
|
" instead of reading whole files. get_entity_info gives a file's referenced types/vars.",
|
|
49522
49725
|
"- Results carry vanillaCache metadata: if available is false, vanilla IDs are missing",
|
|
@@ -212,7 +212,7 @@ class LspProcessHost {
|
|
|
212
212
|
return;
|
|
213
213
|
const watcher = (0, chokidar_1.watch)(this.options.workspaceRoot, {
|
|
214
214
|
ignoreInitial: true,
|
|
215
|
-
ignored: [/(^|[\\/])(?:node_modules|\.
|
|
215
|
+
ignored: [/(^|[\\/])(?:node_modules|\.(?!\.)[^\\/]+)(?:[\\/]|$)/],
|
|
216
216
|
awaitWriteFinish: { stabilityThreshold: 120, pollInterval: 30 },
|
|
217
217
|
});
|
|
218
218
|
watcher.on('add', filePath => this.queueWatchedFileChange(filePath, 1));
|
|
@@ -401,9 +401,7 @@ function isLspWatchedFile(workspaceRoot, filePath, game) {
|
|
|
401
401
|
return false;
|
|
402
402
|
const segments = relative.split(/[\\/]+/).map(segment => segment.toLowerCase());
|
|
403
403
|
if (segments.some(segment => segment === 'node_modules'
|
|
404
|
-
|| segment
|
|
405
|
-
|| segment === '.cwtools'
|
|
406
|
-
|| segment === '.cwtools-ai'))
|
|
404
|
+
|| (segment.startsWith('.') && !segment.startsWith('..'))))
|
|
407
405
|
return false;
|
|
408
406
|
const extension = path.extname(filePath).toLowerCase();
|
|
409
407
|
if (extension === '.csv' && game && game.toLowerCase() !== 'ck2')
|
|
@@ -439,7 +439,7 @@ async function walk(root, visit) {
|
|
|
439
439
|
return;
|
|
440
440
|
const entries = await fs.readdir(root, { withFileTypes: true }).catch(() => []);
|
|
441
441
|
for (const entry of entries) {
|
|
442
|
-
if (entry.name.startsWith('.') && entry.name !== '.cwtools'
|
|
442
|
+
if (entry.name.startsWith('.') && entry.name !== '.cwtools')
|
|
443
443
|
continue;
|
|
444
444
|
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === 'coverage')
|
|
445
445
|
continue;
|
|
@@ -39,7 +39,7 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
// Strong, unambiguous "this directory is a mod root" signals.
|
|
40
40
|
const ROOT_FILE_MARKERS = ['descriptor.mod'];
|
|
41
41
|
const ROOT_NESTED_FILE_MARKERS = [path.join('.metadata', 'metadata.json')];
|
|
42
|
-
const ROOT_DIR_MARKERS = ['.cwtools'
|
|
42
|
+
const ROOT_DIR_MARKERS = ['.cwtools'];
|
|
43
43
|
// PDX content dirs — specific enough at a mod root, but too generic to trust in a
|
|
44
44
|
// far-off ancestor. Generic graphics dirs (gfx/, interface/) are excluded.
|
|
45
45
|
const CONTENT_DIR_MARKERS = ['common', 'events', 'localisation', 'localization'];
|
|
@@ -106,7 +106,6 @@ function parseJsonc(text) {
|
|
|
106
106
|
}
|
|
107
107
|
const cache = new Map();
|
|
108
108
|
const SETTINGS_NAMESPACE = 'stellarisLanguageServices';
|
|
109
|
-
const LEGACY_SETTINGS_NAMESPACE = 'cwtools';
|
|
110
109
|
function readSettingsObject(workspaceRoot) {
|
|
111
110
|
const file = path.join(workspaceRoot, '.vscode', 'settings.json');
|
|
112
111
|
let mtimeMs;
|
|
@@ -152,10 +151,7 @@ function getExtensionSetting(workspaceRoot, subKey) {
|
|
|
152
151
|
const settings = readSettingsObject(workspaceRoot);
|
|
153
152
|
if (!settings)
|
|
154
153
|
return undefined;
|
|
155
|
-
|
|
156
|
-
return current !== undefined
|
|
157
|
-
? current
|
|
158
|
-
: resolveDotted(settings, `${LEGACY_SETTINGS_NAMESPACE}.${subKey}`);
|
|
154
|
+
return resolveDotted(settings, `${SETTINGS_NAMESPACE}.${subKey}`);
|
|
159
155
|
}
|
|
160
156
|
function asNonEmptyStringArray(value) {
|
|
161
157
|
return Array.isArray(value)
|
|
@@ -39,7 +39,7 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
const lspProcessHost_1 = require("./lspProcessHost");
|
|
40
40
|
// File extensions the language server validates — only these are worth revalidating.
|
|
41
41
|
const REVALIDATE_EXTS = new Set(['.txt', '.yml', '.gui', '.gfx', '.asset', '.shader', '.fxh']);
|
|
42
|
-
const SKIP_DIRS = new Set(['node_modules', 'dist', 'coverage'
|
|
42
|
+
const SKIP_DIRS = new Set(['node_modules', 'dist', 'coverage']);
|
|
43
43
|
const MAX_WALK_FILES = 20000;
|
|
44
44
|
const MAX_BATCH = 200;
|
|
45
45
|
function numberOr(value, fallback) {
|
package/dist/mcp/bridgeProxy.js
CHANGED
|
@@ -59,7 +59,7 @@ function createBridgeProxyMcpServer(config) {
|
|
|
59
59
|
'CWTools MCP is served by the active VS Code-compatible extension host.',
|
|
60
60
|
'This proxy does not start its own CWTools language server. If calls report',
|
|
61
61
|
'bridge_unavailable, open the project in the compatible host where the extension',
|
|
62
|
-
'is installed and active, or rerun with --standalone to use the
|
|
62
|
+
'is installed and active, or rerun with --standalone to use the standalone LSP host.',
|
|
63
63
|
].join('\n'),
|
|
64
64
|
});
|
|
65
65
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
@@ -158,7 +158,7 @@ async function readBridgeManifest(config, server) {
|
|
|
158
158
|
}
|
|
159
159
|
if (parsed.kind !== 'cwtools-mcp-extension-bridge' || parsed.protocolVersion !== BRIDGE_PROTOCOL_VERSION) {
|
|
160
160
|
throw new Error(`CWTools MCP bridge manifest at ${manifestPath} is not compatible with this proxy. ` +
|
|
161
|
-
`Open the project in the updated extension host, or pass --standalone
|
|
161
|
+
`Open the project in the updated extension host, or pass --standalone to use the standalone LSP host.`);
|
|
162
162
|
}
|
|
163
163
|
if (!parsed.rpcUrl || !parsed.token) {
|
|
164
164
|
throw new Error(`CWTools MCP bridge manifest at ${manifestPath} is missing rpcUrl/token.`);
|
package/dist/server.js
CHANGED
|
@@ -26,7 +26,7 @@ const SERVER_INSTRUCTIONS = [
|
|
|
26
26
|
'- Before declaring code correct, review get_diagnostics (whole project). Honor the',
|
|
27
27
|
' readiness/freshness fields: if readiness.ready is false the project is still loading —',
|
|
28
28
|
' retry; an empty result then is not authoritative.',
|
|
29
|
-
'- Navigate with
|
|
29
|
+
'- Navigate with go_to_definition / find_references /',
|
|
30
30
|
' document_symbols / workspace_symbols; read structured blocks with get_pdx_block',
|
|
31
31
|
' instead of reading whole files. get_entity_info gives a file\'s referenced types/vars.',
|
|
32
32
|
'- Results carry vanillaCache metadata: if available is false, vanilla IDs are missing',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cwtools-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Read-only MCP server exposing CWTools Paradox/Stellaris semantic tools to external agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
39
|
"chokidar": "^4.0.3",
|
|
40
|
-
"cwtools-shared": "0.2.
|
|
40
|
+
"cwtools-shared": "0.2.5",
|
|
41
41
|
"vscode-jsonrpc": "^8.2.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|