d365fo-mcp 1.0.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.
Files changed (1055) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/dist/bridge/bridgeAdapter.d.ts +319 -0
  4. package/dist/bridge/bridgeAdapter.d.ts.map +1 -0
  5. package/dist/bridge/bridgeAdapter.js +2003 -0
  6. package/dist/bridge/bridgeAdapter.js.map +1 -0
  7. package/dist/bridge/bridgeClient.d.ts +255 -0
  8. package/dist/bridge/bridgeClient.d.ts.map +1 -0
  9. package/dist/bridge/bridgeClient.js +741 -0
  10. package/dist/bridge/bridgeClient.js.map +1 -0
  11. package/dist/bridge/bridgeTypes.d.ts +561 -0
  12. package/dist/bridge/bridgeTypes.d.ts.map +1 -0
  13. package/dist/bridge/bridgeTypes.js +6 -0
  14. package/dist/bridge/bridgeTypes.js.map +1 -0
  15. package/dist/bridge/debouncedRefresh.d.ts +16 -0
  16. package/dist/bridge/debouncedRefresh.d.ts.map +1 -0
  17. package/dist/bridge/debouncedRefresh.js +77 -0
  18. package/dist/bridge/debouncedRefresh.js.map +1 -0
  19. package/dist/bridge/index.d.ts +21 -0
  20. package/dist/bridge/index.d.ts.map +1 -0
  21. package/dist/bridge/index.js +20 -0
  22. package/dist/bridge/index.js.map +1 -0
  23. package/dist/cli/commands/config.d.ts +8 -0
  24. package/dist/cli/commands/config.d.ts.map +1 -0
  25. package/dist/cli/commands/config.js +77 -0
  26. package/dist/cli/commands/config.js.map +1 -0
  27. package/dist/cli/commands/doctor.d.ts +2 -0
  28. package/dist/cli/commands/doctor.d.ts.map +1 -0
  29. package/dist/cli/commands/doctor.js +177 -0
  30. package/dist/cli/commands/doctor.js.map +1 -0
  31. package/dist/cli/commands/indexCmd.d.ts +8 -0
  32. package/dist/cli/commands/indexCmd.d.ts.map +1 -0
  33. package/dist/cli/commands/indexCmd.js +78 -0
  34. package/dist/cli/commands/indexCmd.js.map +1 -0
  35. package/dist/cli/commands/instance.d.ts +4 -0
  36. package/dist/cli/commands/instance.d.ts.map +1 -0
  37. package/dist/cli/commands/instance.js +183 -0
  38. package/dist/cli/commands/instance.js.map +1 -0
  39. package/dist/cli/commands/setup.d.ts +4 -0
  40. package/dist/cli/commands/setup.d.ts.map +1 -0
  41. package/dist/cli/commands/setup.js +246 -0
  42. package/dist/cli/commands/setup.js.map +1 -0
  43. package/dist/cli/commands/start.d.ts +2 -0
  44. package/dist/cli/commands/start.d.ts.map +1 -0
  45. package/dist/cli/commands/start.js +49 -0
  46. package/dist/cli/commands/start.js.map +1 -0
  47. package/dist/cli/commands/update.d.ts +4 -0
  48. package/dist/cli/commands/update.d.ts.map +1 -0
  49. package/dist/cli/commands/update.js +55 -0
  50. package/dist/cli/commands/update.js.map +1 -0
  51. package/dist/cli/context.d.ts +26 -0
  52. package/dist/cli/context.d.ts.map +1 -0
  53. package/dist/cli/context.js +38 -0
  54. package/dist/cli/context.js.map +1 -0
  55. package/dist/cli/envFile.d.ts +23 -0
  56. package/dist/cli/envFile.d.ts.map +1 -0
  57. package/dist/cli/envFile.js +79 -0
  58. package/dist/cli/envFile.js.map +1 -0
  59. package/dist/cli/exec.d.ts +13 -0
  60. package/dist/cli/exec.d.ts.map +1 -0
  61. package/dist/cli/exec.js +47 -0
  62. package/dist/cli/exec.js.map +1 -0
  63. package/dist/cli/index.d.ts +3 -0
  64. package/dist/cli/index.d.ts.map +1 -0
  65. package/dist/cli/index.js +122 -0
  66. package/dist/cli/index.js.map +1 -0
  67. package/dist/cli/instances.d.ts +19 -0
  68. package/dist/cli/instances.d.ts.map +1 -0
  69. package/dist/cli/instances.js +60 -0
  70. package/dist/cli/instances.js.map +1 -0
  71. package/dist/cli/mcpJson.d.ts +15 -0
  72. package/dist/cli/mcpJson.d.ts.map +1 -0
  73. package/dist/cli/mcpJson.js +44 -0
  74. package/dist/cli/mcpJson.js.map +1 -0
  75. package/dist/cli/settingsPrompt.d.ts +20 -0
  76. package/dist/cli/settingsPrompt.d.ts.map +1 -0
  77. package/dist/cli/settingsPrompt.js +150 -0
  78. package/dist/cli/settingsPrompt.js.map +1 -0
  79. package/dist/cli/settingsStore.d.ts +47 -0
  80. package/dist/cli/settingsStore.d.ts.map +1 -0
  81. package/dist/cli/settingsStore.js +144 -0
  82. package/dist/cli/settingsStore.js.map +1 -0
  83. package/dist/cli/target.d.ts +23 -0
  84. package/dist/cli/target.d.ts.map +1 -0
  85. package/dist/cli/target.js +62 -0
  86. package/dist/cli/target.js.map +1 -0
  87. package/dist/cli/ui.d.ts +26 -0
  88. package/dist/cli/ui.d.ts.map +1 -0
  89. package/dist/cli/ui.js +48 -0
  90. package/dist/cli/ui.js.map +1 -0
  91. package/dist/cli/xppConfig.d.ts +35 -0
  92. package/dist/cli/xppConfig.d.ts.map +1 -0
  93. package/dist/cli/xppConfig.js +97 -0
  94. package/dist/cli/xppConfig.js.map +1 -0
  95. package/dist/config/configFile.d.ts +50 -0
  96. package/dist/config/configFile.d.ts.map +1 -0
  97. package/dist/config/configFile.js +152 -0
  98. package/dist/config/configFile.js.map +1 -0
  99. package/dist/config/settings.d.ts +58 -0
  100. package/dist/config/settings.d.ts.map +1 -0
  101. package/dist/config/settings.js +641 -0
  102. package/dist/config/settings.js.map +1 -0
  103. package/dist/database/download.d.ts +27 -0
  104. package/dist/database/download.d.ts.map +1 -0
  105. package/dist/database/download.js +241 -0
  106. package/dist/database/download.js.map +1 -0
  107. package/dist/eval/audit/knowledgeAudit.d.ts +66 -0
  108. package/dist/eval/audit/knowledgeAudit.d.ts.map +1 -0
  109. package/dist/eval/audit/knowledgeAudit.js +117 -0
  110. package/dist/eval/audit/knowledgeAudit.js.map +1 -0
  111. package/dist/eval/audit/knowledgeAuditCli.d.ts +17 -0
  112. package/dist/eval/audit/knowledgeAuditCli.d.ts.map +1 -0
  113. package/dist/eval/audit/knowledgeAuditCli.js +83 -0
  114. package/dist/eval/audit/knowledgeAuditCli.js.map +1 -0
  115. package/dist/eval/audit/knowledgeRefs.d.ts +37 -0
  116. package/dist/eval/audit/knowledgeRefs.d.ts.map +1 -0
  117. package/dist/eval/audit/knowledgeRefs.js +150 -0
  118. package/dist/eval/audit/knowledgeRefs.js.map +1 -0
  119. package/dist/eval/audit/symbolLookup.d.ts +23 -0
  120. package/dist/eval/audit/symbolLookup.d.ts.map +1 -0
  121. package/dist/eval/audit/symbolLookup.js +80 -0
  122. package/dist/eval/audit/symbolLookup.js.map +1 -0
  123. package/dist/eval/coverage/coverage.d.ts +65 -0
  124. package/dist/eval/coverage/coverage.d.ts.map +1 -0
  125. package/dist/eval/coverage/coverage.js +136 -0
  126. package/dist/eval/coverage/coverage.js.map +1 -0
  127. package/dist/eval/coverage/coverageCli.d.ts +13 -0
  128. package/dist/eval/coverage/coverageCli.d.ts.map +1 -0
  129. package/dist/eval/coverage/coverageCli.js +106 -0
  130. package/dist/eval/coverage/coverageCli.js.map +1 -0
  131. package/dist/eval/coverage/sources.d.ts +17 -0
  132. package/dist/eval/coverage/sources.d.ts.map +1 -0
  133. package/dist/eval/coverage/sources.js +40 -0
  134. package/dist/eval/coverage/sources.js.map +1 -0
  135. package/dist/eval/coverage/taxonomy.d.ts +59 -0
  136. package/dist/eval/coverage/taxonomy.d.ts.map +1 -0
  137. package/dist/eval/coverage/taxonomy.js +400 -0
  138. package/dist/eval/coverage/taxonomy.js.map +1 -0
  139. package/dist/eval/improver/caseMining.d.ts +55 -0
  140. package/dist/eval/improver/caseMining.d.ts.map +1 -0
  141. package/dist/eval/improver/caseMining.js +64 -0
  142. package/dist/eval/improver/caseMining.js.map +1 -0
  143. package/dist/eval/improver/caseMiningCli.d.ts +10 -0
  144. package/dist/eval/improver/caseMiningCli.d.ts.map +1 -0
  145. package/dist/eval/improver/caseMiningCli.js +50 -0
  146. package/dist/eval/improver/caseMiningCli.js.map +1 -0
  147. package/dist/eval/improver/cli.d.ts +11 -0
  148. package/dist/eval/improver/cli.d.ts.map +1 -0
  149. package/dist/eval/improver/cli.js +73 -0
  150. package/dist/eval/improver/cli.js.map +1 -0
  151. package/dist/eval/improver/cluster.d.ts +47 -0
  152. package/dist/eval/improver/cluster.d.ts.map +1 -0
  153. package/dist/eval/improver/cluster.js +0 -0
  154. package/dist/eval/improver/cluster.js.map +1 -0
  155. package/dist/eval/improver/corpusIO.d.ts +26 -0
  156. package/dist/eval/improver/corpusIO.d.ts.map +1 -0
  157. package/dist/eval/improver/corpusIO.js +50 -0
  158. package/dist/eval/improver/corpusIO.js.map +1 -0
  159. package/dist/eval/improver/fixBrief.d.ts +34 -0
  160. package/dist/eval/improver/fixBrief.d.ts.map +1 -0
  161. package/dist/eval/improver/fixBrief.js +69 -0
  162. package/dist/eval/improver/fixBrief.js.map +1 -0
  163. package/dist/eval/improver/fixBriefCli.d.ts +11 -0
  164. package/dist/eval/improver/fixBriefCli.d.ts.map +1 -0
  165. package/dist/eval/improver/fixBriefCli.js +46 -0
  166. package/dist/eval/improver/fixBriefCli.js.map +1 -0
  167. package/dist/eval/improver/flakeDetection.d.ts +50 -0
  168. package/dist/eval/improver/flakeDetection.d.ts.map +1 -0
  169. package/dist/eval/improver/flakeDetection.js +0 -0
  170. package/dist/eval/improver/flakeDetection.js.map +1 -0
  171. package/dist/eval/improver/flakeDetectionCli.d.ts +8 -0
  172. package/dist/eval/improver/flakeDetectionCli.d.ts.map +1 -0
  173. package/dist/eval/improver/flakeDetectionCli.js +27 -0
  174. package/dist/eval/improver/flakeDetectionCli.js.map +1 -0
  175. package/dist/eval/improver/heldout.d.ts +44 -0
  176. package/dist/eval/improver/heldout.d.ts.map +1 -0
  177. package/dist/eval/improver/heldout.js +52 -0
  178. package/dist/eval/improver/heldout.js.map +1 -0
  179. package/dist/eval/improver/knowledgeFeedback.d.ts +56 -0
  180. package/dist/eval/improver/knowledgeFeedback.d.ts.map +1 -0
  181. package/dist/eval/improver/knowledgeFeedback.js +88 -0
  182. package/dist/eval/improver/knowledgeFeedback.js.map +1 -0
  183. package/dist/eval/improver/knowledgeFeedbackCli.d.ts +8 -0
  184. package/dist/eval/improver/knowledgeFeedbackCli.d.ts.map +1 -0
  185. package/dist/eval/improver/knowledgeFeedbackCli.js +28 -0
  186. package/dist/eval/improver/knowledgeFeedbackCli.js.map +1 -0
  187. package/dist/eval/improver/report.d.ts +36 -0
  188. package/dist/eval/improver/report.d.ts.map +1 -0
  189. package/dist/eval/improver/report.js +61 -0
  190. package/dist/eval/improver/report.js.map +1 -0
  191. package/dist/eval/improver/reportCli.d.ts +7 -0
  192. package/dist/eval/improver/reportCli.d.ts.map +1 -0
  193. package/dist/eval/improver/reportCli.js +27 -0
  194. package/dist/eval/improver/reportCli.js.map +1 -0
  195. package/dist/eval/oracle/actualArtifactResolution.d.ts +49 -0
  196. package/dist/eval/oracle/actualArtifactResolution.d.ts.map +1 -0
  197. package/dist/eval/oracle/actualArtifactResolution.js +73 -0
  198. package/dist/eval/oracle/actualArtifactResolution.js.map +1 -0
  199. package/dist/eval/oracle/cli.d.ts +30 -0
  200. package/dist/eval/oracle/cli.d.ts.map +1 -0
  201. package/dist/eval/oracle/cli.js +179 -0
  202. package/dist/eval/oracle/cli.js.map +1 -0
  203. package/dist/eval/oracle/diff.d.ts +26 -0
  204. package/dist/eval/oracle/diff.d.ts.map +1 -0
  205. package/dist/eval/oracle/diff.js +51 -0
  206. package/dist/eval/oracle/diff.js.map +1 -0
  207. package/dist/eval/oracle/index.d.ts +81 -0
  208. package/dist/eval/oracle/index.d.ts.map +1 -0
  209. package/dist/eval/oracle/index.js +55 -0
  210. package/dist/eval/oracle/index.js.map +1 -0
  211. package/dist/eval/oracle/normalize.d.ts +85 -0
  212. package/dist/eval/oracle/normalize.d.ts.map +1 -0
  213. package/dist/eval/oracle/normalize.js +321 -0
  214. package/dist/eval/oracle/normalize.js.map +1 -0
  215. package/dist/eval/oracle/score.d.ts +26 -0
  216. package/dist/eval/oracle/score.d.ts.map +1 -0
  217. package/dist/eval/oracle/score.js +15 -0
  218. package/dist/eval/oracle/score.js.map +1 -0
  219. package/dist/eval/oracle/systest.d.ts +30 -0
  220. package/dist/eval/oracle/systest.d.ts.map +1 -0
  221. package/dist/eval/oracle/systest.js +98 -0
  222. package/dist/eval/oracle/systest.js.map +1 -0
  223. package/dist/index.d.ts +6 -0
  224. package/dist/index.d.ts.map +1 -0
  225. package/dist/index.js +753 -0
  226. package/dist/index.js.map +1 -0
  227. package/dist/knowledge/formPatterns/catalog/subPatterns/customFilters.d.ts +10 -0
  228. package/dist/knowledge/formPatterns/catalog/subPatterns/customFilters.d.ts.map +1 -0
  229. package/dist/knowledge/formPatterns/catalog/subPatterns/customFilters.js +38 -0
  230. package/dist/knowledge/formPatterns/catalog/subPatterns/customFilters.js.map +1 -0
  231. package/dist/knowledge/formPatterns/catalog/subPatterns/fields.d.ts +21 -0
  232. package/dist/knowledge/formPatterns/catalog/subPatterns/fields.d.ts.map +1 -0
  233. package/dist/knowledge/formPatterns/catalog/subPatterns/fields.js +168 -0
  234. package/dist/knowledge/formPatterns/catalog/subPatterns/fields.js.map +1 -0
  235. package/dist/knowledge/formPatterns/catalog/subPatterns/panels.d.ts +9 -0
  236. package/dist/knowledge/formPatterns/catalog/subPatterns/panels.d.ts.map +1 -0
  237. package/dist/knowledge/formPatterns/catalog/subPatterns/panels.js +47 -0
  238. package/dist/knowledge/formPatterns/catalog/subPatterns/panels.js.map +1 -0
  239. package/dist/knowledge/formPatterns/catalog/subPatterns/toolbar.d.ts +16 -0
  240. package/dist/knowledge/formPatterns/catalog/subPatterns/toolbar.d.ts.map +1 -0
  241. package/dist/knowledge/formPatterns/catalog/subPatterns/toolbar.js +124 -0
  242. package/dist/knowledge/formPatterns/catalog/subPatterns/toolbar.js.map +1 -0
  243. package/dist/knowledge/formPatterns/catalog/subPatterns/workspaceSections.d.ts +12 -0
  244. package/dist/knowledge/formPatterns/catalog/subPatterns/workspaceSections.d.ts.map +1 -0
  245. package/dist/knowledge/formPatterns/catalog/subPatterns/workspaceSections.js +36 -0
  246. package/dist/knowledge/formPatterns/catalog/subPatterns/workspaceSections.js.map +1 -0
  247. package/dist/knowledge/formPatterns/catalog/topLevel/common.d.ts +11 -0
  248. package/dist/knowledge/formPatterns/catalog/topLevel/common.d.ts.map +1 -0
  249. package/dist/knowledge/formPatterns/catalog/topLevel/common.js +37 -0
  250. package/dist/knowledge/formPatterns/catalog/topLevel/common.js.map +1 -0
  251. package/dist/knowledge/formPatterns/catalog/topLevel/detailsMaster.d.ts +8 -0
  252. package/dist/knowledge/formPatterns/catalog/topLevel/detailsMaster.d.ts.map +1 -0
  253. package/dist/knowledge/formPatterns/catalog/topLevel/detailsMaster.js +86 -0
  254. package/dist/knowledge/formPatterns/catalog/topLevel/detailsMaster.js.map +1 -0
  255. package/dist/knowledge/formPatterns/catalog/topLevel/detailsTransaction.d.ts +7 -0
  256. package/dist/knowledge/formPatterns/catalog/topLevel/detailsTransaction.d.ts.map +1 -0
  257. package/dist/knowledge/formPatterns/catalog/topLevel/detailsTransaction.js +69 -0
  258. package/dist/knowledge/formPatterns/catalog/topLevel/detailsTransaction.js.map +1 -0
  259. package/dist/knowledge/formPatterns/catalog/topLevel/dialog.d.ts +14 -0
  260. package/dist/knowledge/formPatterns/catalog/topLevel/dialog.d.ts.map +1 -0
  261. package/dist/knowledge/formPatterns/catalog/topLevel/dialog.js +187 -0
  262. package/dist/knowledge/formPatterns/catalog/topLevel/dialog.js.map +1 -0
  263. package/dist/knowledge/formPatterns/catalog/topLevel/factBox.d.ts +10 -0
  264. package/dist/knowledge/formPatterns/catalog/topLevel/factBox.d.ts.map +1 -0
  265. package/dist/knowledge/formPatterns/catalog/topLevel/factBox.js +54 -0
  266. package/dist/knowledge/formPatterns/catalog/topLevel/factBox.js.map +1 -0
  267. package/dist/knowledge/formPatterns/catalog/topLevel/listPage.d.ts +7 -0
  268. package/dist/knowledge/formPatterns/catalog/topLevel/listPage.d.ts.map +1 -0
  269. package/dist/knowledge/formPatterns/catalog/topLevel/listPage.js +33 -0
  270. package/dist/knowledge/formPatterns/catalog/topLevel/listPage.js.map +1 -0
  271. package/dist/knowledge/formPatterns/catalog/topLevel/lookup.d.ts +10 -0
  272. package/dist/knowledge/formPatterns/catalog/topLevel/lookup.d.ts.map +1 -0
  273. package/dist/knowledge/formPatterns/catalog/topLevel/lookup.js +95 -0
  274. package/dist/knowledge/formPatterns/catalog/topLevel/lookup.js.map +1 -0
  275. package/dist/knowledge/formPatterns/catalog/topLevel/simpleDetails.d.ts +16 -0
  276. package/dist/knowledge/formPatterns/catalog/topLevel/simpleDetails.d.ts.map +1 -0
  277. package/dist/knowledge/formPatterns/catalog/topLevel/simpleDetails.js +158 -0
  278. package/dist/knowledge/formPatterns/catalog/topLevel/simpleDetails.js.map +1 -0
  279. package/dist/knowledge/formPatterns/catalog/topLevel/simpleList.d.ts +7 -0
  280. package/dist/knowledge/formPatterns/catalog/topLevel/simpleList.d.ts.map +1 -0
  281. package/dist/knowledge/formPatterns/catalog/topLevel/simpleList.js +34 -0
  282. package/dist/knowledge/formPatterns/catalog/topLevel/simpleList.js.map +1 -0
  283. package/dist/knowledge/formPatterns/catalog/topLevel/simpleListDetails.d.ts +9 -0
  284. package/dist/knowledge/formPatterns/catalog/topLevel/simpleListDetails.d.ts.map +1 -0
  285. package/dist/knowledge/formPatterns/catalog/topLevel/simpleListDetails.js +99 -0
  286. package/dist/knowledge/formPatterns/catalog/topLevel/simpleListDetails.js.map +1 -0
  287. package/dist/knowledge/formPatterns/catalog/topLevel/tableOfContents.d.ts +8 -0
  288. package/dist/knowledge/formPatterns/catalog/topLevel/tableOfContents.d.ts.map +1 -0
  289. package/dist/knowledge/formPatterns/catalog/topLevel/tableOfContents.js +95 -0
  290. package/dist/knowledge/formPatterns/catalog/topLevel/tableOfContents.js.map +1 -0
  291. package/dist/knowledge/formPatterns/catalog/topLevel/task.d.ts +9 -0
  292. package/dist/knowledge/formPatterns/catalog/topLevel/task.d.ts.map +1 -0
  293. package/dist/knowledge/formPatterns/catalog/topLevel/task.js +42 -0
  294. package/dist/knowledge/formPatterns/catalog/topLevel/task.js.map +1 -0
  295. package/dist/knowledge/formPatterns/catalog/topLevel/wizard.d.ts +7 -0
  296. package/dist/knowledge/formPatterns/catalog/topLevel/wizard.d.ts.map +1 -0
  297. package/dist/knowledge/formPatterns/catalog/topLevel/wizard.js +42 -0
  298. package/dist/knowledge/formPatterns/catalog/topLevel/wizard.js.map +1 -0
  299. package/dist/knowledge/formPatterns/catalog/topLevel/workspace.d.ts +17 -0
  300. package/dist/knowledge/formPatterns/catalog/topLevel/workspace.d.ts.map +1 -0
  301. package/dist/knowledge/formPatterns/catalog/topLevel/workspace.js +157 -0
  302. package/dist/knowledge/formPatterns/catalog/topLevel/workspace.js.map +1 -0
  303. package/dist/knowledge/formPatterns/crossCheck.d.ts +44 -0
  304. package/dist/knowledge/formPatterns/crossCheck.d.ts.map +1 -0
  305. package/dist/knowledge/formPatterns/crossCheck.js +109 -0
  306. package/dist/knowledge/formPatterns/crossCheck.js.map +1 -0
  307. package/dist/knowledge/formPatterns/index.d.ts +30 -0
  308. package/dist/knowledge/formPatterns/index.d.ts.map +1 -0
  309. package/dist/knowledge/formPatterns/index.js +158 -0
  310. package/dist/knowledge/formPatterns/index.js.map +1 -0
  311. package/dist/knowledge/formPatterns/methodStubs.d.ts +45 -0
  312. package/dist/knowledge/formPatterns/methodStubs.d.ts.map +1 -0
  313. package/dist/knowledge/formPatterns/methodStubs.js +257 -0
  314. package/dist/knowledge/formPatterns/methodStubs.js.map +1 -0
  315. package/dist/knowledge/formPatterns/types.d.ts +115 -0
  316. package/dist/knowledge/formPatterns/types.d.ts.map +1 -0
  317. package/dist/knowledge/formPatterns/types.js +44 -0
  318. package/dist/knowledge/formPatterns/types.js.map +1 -0
  319. package/dist/metadata/enhancedParser.d.ts +91 -0
  320. package/dist/metadata/enhancedParser.d.ts.map +1 -0
  321. package/dist/metadata/enhancedParser.js +379 -0
  322. package/dist/metadata/enhancedParser.js.map +1 -0
  323. package/dist/metadata/formPatternMiner.d.ts +78 -0
  324. package/dist/metadata/formPatternMiner.d.ts.map +1 -0
  325. package/dist/metadata/formPatternMiner.js +220 -0
  326. package/dist/metadata/formPatternMiner.js.map +1 -0
  327. package/dist/metadata/labelParser.d.ts +71 -0
  328. package/dist/metadata/labelParser.d.ts.map +1 -0
  329. package/dist/metadata/labelParser.js +258 -0
  330. package/dist/metadata/labelParser.js.map +1 -0
  331. package/dist/metadata/symbolCountsWorker.d.ts +16 -0
  332. package/dist/metadata/symbolCountsWorker.d.ts.map +1 -0
  333. package/dist/metadata/symbolCountsWorker.js +42 -0
  334. package/dist/metadata/symbolCountsWorker.js.map +1 -0
  335. package/dist/metadata/symbolIndex.d.ts +474 -0
  336. package/dist/metadata/symbolIndex.d.ts.map +1 -0
  337. package/dist/metadata/symbolIndex.js +3152 -0
  338. package/dist/metadata/symbolIndex.js.map +1 -0
  339. package/dist/metadata/types.d.ts +267 -0
  340. package/dist/metadata/types.d.ts.map +1 -0
  341. package/dist/metadata/types.js +5 -0
  342. package/dist/metadata/types.js.map +1 -0
  343. package/dist/metadata/xmlParser.d.ts +232 -0
  344. package/dist/metadata/xmlParser.d.ts.map +1 -0
  345. package/dist/metadata/xmlParser.js +1021 -0
  346. package/dist/metadata/xmlParser.js.map +1 -0
  347. package/dist/metadata/xppDeclaration.d.ts +116 -0
  348. package/dist/metadata/xppDeclaration.d.ts.map +1 -0
  349. package/dist/metadata/xppDeclaration.js +350 -0
  350. package/dist/metadata/xppDeclaration.js.map +1 -0
  351. package/dist/middleware/apiKeyAuth.d.ts +7 -0
  352. package/dist/middleware/apiKeyAuth.d.ts.map +1 -0
  353. package/dist/middleware/apiKeyAuth.js +70 -0
  354. package/dist/middleware/apiKeyAuth.js.map +1 -0
  355. package/dist/middleware/rateLimiter.d.ts +7 -0
  356. package/dist/middleware/rateLimiter.d.ts.map +1 -0
  357. package/dist/middleware/rateLimiter.js +78 -0
  358. package/dist/middleware/rateLimiter.js.map +1 -0
  359. package/dist/prompts/codeReview.d.ts +8 -0
  360. package/dist/prompts/codeReview.d.ts.map +1 -0
  361. package/dist/prompts/codeReview.js +619 -0
  362. package/dist/prompts/codeReview.js.map +1 -0
  363. package/dist/prompts/index.d.ts +6 -0
  364. package/dist/prompts/index.d.ts.map +1 -0
  365. package/dist/prompts/index.js +6 -0
  366. package/dist/prompts/index.js.map +1 -0
  367. package/dist/prompts/systemInstructions.d.ts +33 -0
  368. package/dist/prompts/systemInstructions.d.ts.map +1 -0
  369. package/dist/prompts/systemInstructions.js +154 -0
  370. package/dist/prompts/systemInstructions.js.map +1 -0
  371. package/dist/resources/classResource.d.ts +17 -0
  372. package/dist/resources/classResource.d.ts.map +1 -0
  373. package/dist/resources/classResource.js +33 -0
  374. package/dist/resources/classResource.js.map +1 -0
  375. package/dist/resources/index.d.ts +20 -0
  376. package/dist/resources/index.d.ts.map +1 -0
  377. package/dist/resources/index.js +154 -0
  378. package/dist/resources/index.js.map +1 -0
  379. package/dist/server/mcpServer.d.ts +11 -0
  380. package/dist/server/mcpServer.d.ts.map +1 -0
  381. package/dist/server/mcpServer.js +185 -0
  382. package/dist/server/mcpServer.js.map +1 -0
  383. package/dist/server/serverMode.d.ts +53 -0
  384. package/dist/server/serverMode.d.ts.map +1 -0
  385. package/dist/server/serverMode.js +72 -0
  386. package/dist/server/serverMode.js.map +1 -0
  387. package/dist/server/toolAnnotations.d.ts +28 -0
  388. package/dist/server/toolAnnotations.d.ts.map +1 -0
  389. package/dist/server/toolAnnotations.js +70 -0
  390. package/dist/server/toolAnnotations.js.map +1 -0
  391. package/dist/server/toolSchemas/analyzeCode.d.ts +71 -0
  392. package/dist/server/toolSchemas/analyzeCode.d.ts.map +1 -0
  393. package/dist/server/toolSchemas/analyzeCode.js +50 -0
  394. package/dist/server/toolSchemas/analyzeCode.js.map +1 -0
  395. package/dist/server/toolSchemas/batchGetInfo.d.ts +32 -0
  396. package/dist/server/toolSchemas/batchGetInfo.d.ts.map +1 -0
  397. package/dist/server/toolSchemas/batchGetInfo.js +35 -0
  398. package/dist/server/toolSchemas/batchGetInfo.js.map +1 -0
  399. package/dist/server/toolSchemas/buildD365foProject.d.ts +44 -0
  400. package/dist/server/toolSchemas/buildD365foProject.d.ts.map +1 -0
  401. package/dist/server/toolSchemas/buildD365foProject.js +47 -0
  402. package/dist/server/toolSchemas/buildD365foProject.js.map +1 -0
  403. package/dist/server/toolSchemas/d365foFile.d.ts +99 -0
  404. package/dist/server/toolSchemas/d365foFile.d.ts.map +1 -0
  405. package/dist/server/toolSchemas/d365foFile.js +163 -0
  406. package/dist/server/toolSchemas/d365foFile.js.map +1 -0
  407. package/dist/server/toolSchemas/extensionInfo.d.ts +65 -0
  408. package/dist/server/toolSchemas/extensionInfo.d.ts.map +1 -0
  409. package/dist/server/toolSchemas/extensionInfo.js +73 -0
  410. package/dist/server/toolSchemas/extensionInfo.js.map +1 -0
  411. package/dist/server/toolSchemas/findReferences.d.ts +35 -0
  412. package/dist/server/toolSchemas/findReferences.d.ts.map +1 -0
  413. package/dist/server/toolSchemas/findReferences.js +35 -0
  414. package/dist/server/toolSchemas/findReferences.js.map +1 -0
  415. package/dist/server/toolSchemas/generateObject.d.ts +246 -0
  416. package/dist/server/toolSchemas/generateObject.d.ts.map +1 -0
  417. package/dist/server/toolSchemas/generateObject.js +158 -0
  418. package/dist/server/toolSchemas/generateObject.js.map +1 -0
  419. package/dist/server/toolSchemas/getKnowledge.d.ts +39 -0
  420. package/dist/server/toolSchemas/getKnowledge.d.ts.map +1 -0
  421. package/dist/server/toolSchemas/getKnowledge.js +46 -0
  422. package/dist/server/toolSchemas/getKnowledge.js.map +1 -0
  423. package/dist/server/toolSchemas/getMethod.d.ts +30 -0
  424. package/dist/server/toolSchemas/getMethod.d.ts.map +1 -0
  425. package/dist/server/toolSchemas/getMethod.js +34 -0
  426. package/dist/server/toolSchemas/getMethod.js.map +1 -0
  427. package/dist/server/toolSchemas/getObjectInfo.d.ts +24 -0
  428. package/dist/server/toolSchemas/getObjectInfo.d.ts.map +1 -0
  429. package/dist/server/toolSchemas/getObjectInfo.js +30 -0
  430. package/dist/server/toolSchemas/getObjectInfo.js.map +1 -0
  431. package/dist/server/toolSchemas/getWorkspaceInfo.d.ts +29 -0
  432. package/dist/server/toolSchemas/getWorkspaceInfo.d.ts.map +1 -0
  433. package/dist/server/toolSchemas/getWorkspaceInfo.js +29 -0
  434. package/dist/server/toolSchemas/getWorkspaceInfo.js.map +1 -0
  435. package/dist/server/toolSchemas/index.d.ts +1142 -0
  436. package/dist/server/toolSchemas/index.d.ts.map +1 -0
  437. package/dist/server/toolSchemas/index.js +60 -0
  438. package/dist/server/toolSchemas/index.js.map +1 -0
  439. package/dist/server/toolSchemas/labels.d.ts +172 -0
  440. package/dist/server/toolSchemas/labels.d.ts.map +1 -0
  441. package/dist/server/toolSchemas/labels.js +159 -0
  442. package/dist/server/toolSchemas/labels.js.map +1 -0
  443. package/dist/server/toolSchemas/objectPatterns.d.ts +93 -0
  444. package/dist/server/toolSchemas/objectPatterns.d.ts.map +1 -0
  445. package/dist/server/toolSchemas/objectPatterns.js +104 -0
  446. package/dist/server/toolSchemas/objectPatterns.js.map +1 -0
  447. package/dist/server/toolSchemas/prepare.d.ts +49 -0
  448. package/dist/server/toolSchemas/prepare.d.ts.map +1 -0
  449. package/dist/server/toolSchemas/prepare.js +57 -0
  450. package/dist/server/toolSchemas/prepare.js.map +1 -0
  451. package/dist/server/toolSchemas/reviewWorkspaceChanges.d.ts +20 -0
  452. package/dist/server/toolSchemas/reviewWorkspaceChanges.d.ts.map +1 -0
  453. package/dist/server/toolSchemas/reviewWorkspaceChanges.js +19 -0
  454. package/dist/server/toolSchemas/reviewWorkspaceChanges.js.map +1 -0
  455. package/dist/server/toolSchemas/runBpCheck.d.ts +36 -0
  456. package/dist/server/toolSchemas/runBpCheck.d.ts.map +1 -0
  457. package/dist/server/toolSchemas/runBpCheck.js +21 -0
  458. package/dist/server/toolSchemas/runBpCheck.js.map +1 -0
  459. package/dist/server/toolSchemas/runSystestClass.d.ts +32 -0
  460. package/dist/server/toolSchemas/runSystestClass.d.ts.map +1 -0
  461. package/dist/server/toolSchemas/runSystestClass.js +20 -0
  462. package/dist/server/toolSchemas/runSystestClass.js.map +1 -0
  463. package/dist/server/toolSchemas/search.d.ts +109 -0
  464. package/dist/server/toolSchemas/search.d.ts.map +1 -0
  465. package/dist/server/toolSchemas/search.js +123 -0
  466. package/dist/server/toolSchemas/search.js.map +1 -0
  467. package/dist/server/toolSchemas/securityInfo.d.ts +45 -0
  468. package/dist/server/toolSchemas/securityInfo.d.ts.map +1 -0
  469. package/dist/server/toolSchemas/securityInfo.js +39 -0
  470. package/dist/server/toolSchemas/securityInfo.js.map +1 -0
  471. package/dist/server/toolSchemas/suggestEdt.d.ts +29 -0
  472. package/dist/server/toolSchemas/suggestEdt.d.ts.map +1 -0
  473. package/dist/server/toolSchemas/suggestEdt.js +29 -0
  474. package/dist/server/toolSchemas/suggestEdt.js.map +1 -0
  475. package/dist/server/toolSchemas/triggerDbSync.d.ts +47 -0
  476. package/dist/server/toolSchemas/triggerDbSync.d.ts.map +1 -0
  477. package/dist/server/toolSchemas/triggerDbSync.js +31 -0
  478. package/dist/server/toolSchemas/triggerDbSync.js.map +1 -0
  479. package/dist/server/toolSchemas/undoLastModification.d.ts +20 -0
  480. package/dist/server/toolSchemas/undoLastModification.d.ts.map +1 -0
  481. package/dist/server/toolSchemas/undoLastModification.js +17 -0
  482. package/dist/server/toolSchemas/undoLastModification.js.map +1 -0
  483. package/dist/server/toolSchemas/updateSymbolIndex.d.ts +19 -0
  484. package/dist/server/toolSchemas/updateSymbolIndex.d.ts.map +1 -0
  485. package/dist/server/toolSchemas/updateSymbolIndex.js +18 -0
  486. package/dist/server/toolSchemas/updateSymbolIndex.js.map +1 -0
  487. package/dist/server/toolSchemas/validateCode.d.ts +35 -0
  488. package/dist/server/toolSchemas/validateCode.d.ts.map +1 -0
  489. package/dist/server/toolSchemas/validateCode.js +38 -0
  490. package/dist/server/toolSchemas/validateCode.js.map +1 -0
  491. package/dist/server/toolSchemas/validateObjectNaming.d.ts +33 -0
  492. package/dist/server/toolSchemas/validateObjectNaming.d.ts.map +1 -0
  493. package/dist/server/toolSchemas/validateObjectNaming.js +32 -0
  494. package/dist/server/toolSchemas/validateObjectNaming.js.map +1 -0
  495. package/dist/server/toolSchemas/verifyD365foProject.d.ts +50 -0
  496. package/dist/server/toolSchemas/verifyD365foProject.d.ts.map +1 -0
  497. package/dist/server/toolSchemas/verifyD365foProject.js +46 -0
  498. package/dist/server/toolSchemas/verifyD365foProject.js.map +1 -0
  499. package/dist/server/transport.d.ts +30 -0
  500. package/dist/server/transport.d.ts.map +1 -0
  501. package/dist/server/transport.js +391 -0
  502. package/dist/server/transport.js.map +1 -0
  503. package/dist/tools/analyzeCode.d.ts +17 -0
  504. package/dist/tools/analyzeCode.d.ts.map +1 -0
  505. package/dist/tools/analyzeCode.js +63 -0
  506. package/dist/tools/analyzeCode.js.map +1 -0
  507. package/dist/tools/analyzeCompleteness.d.ts +20 -0
  508. package/dist/tools/analyzeCompleteness.d.ts.map +1 -0
  509. package/dist/tools/analyzeCompleteness.js +103 -0
  510. package/dist/tools/analyzeCompleteness.js.map +1 -0
  511. package/dist/tools/analyzeExtensionPoints.d.ts +21 -0
  512. package/dist/tools/analyzeExtensionPoints.d.ts.map +1 -0
  513. package/dist/tools/analyzeExtensionPoints.js +343 -0
  514. package/dist/tools/analyzeExtensionPoints.js.map +1 -0
  515. package/dist/tools/analyzePatterns.d.ts +20 -0
  516. package/dist/tools/analyzePatterns.d.ts.map +1 -0
  517. package/dist/tools/analyzePatterns.js +71 -0
  518. package/dist/tools/analyzePatterns.js.map +1 -0
  519. package/dist/tools/apiUsagePatterns.d.ts +20 -0
  520. package/dist/tools/apiUsagePatterns.d.ts.map +1 -0
  521. package/dist/tools/apiUsagePatterns.js +136 -0
  522. package/dist/tools/apiUsagePatterns.js.map +1 -0
  523. package/dist/tools/batchGetInfo.d.ts +50 -0
  524. package/dist/tools/batchGetInfo.d.ts.map +1 -0
  525. package/dist/tools/batchGetInfo.js +52 -0
  526. package/dist/tools/batchGetInfo.js.map +1 -0
  527. package/dist/tools/batchSearch.d.ts +64 -0
  528. package/dist/tools/batchSearch.d.ts.map +1 -0
  529. package/dist/tools/batchSearch.js +265 -0
  530. package/dist/tools/batchSearch.js.map +1 -0
  531. package/dist/tools/buildProject.d.ts +28 -0
  532. package/dist/tools/buildProject.d.ts.map +1 -0
  533. package/dist/tools/buildProject.js +929 -0
  534. package/dist/tools/buildProject.js.map +1 -0
  535. package/dist/tools/classInfo.d.ts +8 -0
  536. package/dist/tools/classInfo.d.ts.map +1 -0
  537. package/dist/tools/classInfo.js +235 -0
  538. package/dist/tools/classInfo.js.map +1 -0
  539. package/dist/tools/codeGen.d.ts +19 -0
  540. package/dist/tools/codeGen.d.ts.map +1 -0
  541. package/dist/tools/codeGen.js +1956 -0
  542. package/dist/tools/codeGen.js.map +1 -0
  543. package/dist/tools/completion.d.ts +8 -0
  544. package/dist/tools/completion.d.ts.map +1 -0
  545. package/dist/tools/completion.js +272 -0
  546. package/dist/tools/completion.js.map +1 -0
  547. package/dist/tools/configKeyInfo.d.ts +22 -0
  548. package/dist/tools/configKeyInfo.d.ts.map +1 -0
  549. package/dist/tools/configKeyInfo.js +74 -0
  550. package/dist/tools/configKeyInfo.js.map +1 -0
  551. package/dist/tools/createD365File.d.ts +479 -0
  552. package/dist/tools/createD365File.d.ts.map +1 -0
  553. package/dist/tools/createD365File.js +4185 -0
  554. package/dist/tools/createD365File.js.map +1 -0
  555. package/dist/tools/createLabel.d.ts +51 -0
  556. package/dist/tools/createLabel.d.ts.map +1 -0
  557. package/dist/tools/createLabel.js +761 -0
  558. package/dist/tools/createLabel.js.map +1 -0
  559. package/dist/tools/d365foErrorHelp.d.ts +29 -0
  560. package/dist/tools/d365foErrorHelp.d.ts.map +1 -0
  561. package/dist/tools/d365foErrorHelp.js +389 -0
  562. package/dist/tools/d365foErrorHelp.js.map +1 -0
  563. package/dist/tools/d365foFile.d.ts +20 -0
  564. package/dist/tools/d365foFile.d.ts.map +1 -0
  565. package/dist/tools/d365foFile.js +58 -0
  566. package/dist/tools/d365foFile.js.map +1 -0
  567. package/dist/tools/d365foFileOpSpecs.d.ts +41 -0
  568. package/dist/tools/d365foFileOpSpecs.d.ts.map +1 -0
  569. package/dist/tools/d365foFileOpSpecs.js +336 -0
  570. package/dist/tools/d365foFileOpSpecs.js.map +1 -0
  571. package/dist/tools/dataEntityInfo.d.ts +23 -0
  572. package/dist/tools/dataEntityInfo.d.ts.map +1 -0
  573. package/dist/tools/dataEntityInfo.js +45 -0
  574. package/dist/tools/dataEntityInfo.js.map +1 -0
  575. package/dist/tools/dataEntityXml.d.ts +31 -0
  576. package/dist/tools/dataEntityXml.d.ts.map +1 -0
  577. package/dist/tools/dataEntityXml.js +131 -0
  578. package/dist/tools/dataEntityXml.js.map +1 -0
  579. package/dist/tools/dbSync.d.ts +8 -0
  580. package/dist/tools/dbSync.d.ts.map +1 -0
  581. package/dist/tools/dbSync.js +285 -0
  582. package/dist/tools/dbSync.js.map +1 -0
  583. package/dist/tools/edtInfo.d.ts +23 -0
  584. package/dist/tools/edtInfo.d.ts.map +1 -0
  585. package/dist/tools/edtInfo.js +122 -0
  586. package/dist/tools/edtInfo.js.map +1 -0
  587. package/dist/tools/enumInfo.d.ts +17 -0
  588. package/dist/tools/enumInfo.d.ts.map +1 -0
  589. package/dist/tools/enumInfo.js +44 -0
  590. package/dist/tools/enumInfo.js.map +1 -0
  591. package/dist/tools/extensionInfo.d.ts +26 -0
  592. package/dist/tools/extensionInfo.d.ts.map +1 -0
  593. package/dist/tools/extensionInfo.js +73 -0
  594. package/dist/tools/extensionInfo.js.map +1 -0
  595. package/dist/tools/extensionSearch.d.ts +20 -0
  596. package/dist/tools/extensionSearch.d.ts.map +1 -0
  597. package/dist/tools/extensionSearch.js +72 -0
  598. package/dist/tools/extensionSearch.js.map +1 -0
  599. package/dist/tools/extensionStrategyAdvisor.d.ts +15 -0
  600. package/dist/tools/extensionStrategyAdvisor.d.ts.map +1 -0
  601. package/dist/tools/extensionStrategyAdvisor.js +519 -0
  602. package/dist/tools/extensionStrategyAdvisor.js.map +1 -0
  603. package/dist/tools/findCocExtensions.d.ts +20 -0
  604. package/dist/tools/findCocExtensions.d.ts.map +1 -0
  605. package/dist/tools/findCocExtensions.js +285 -0
  606. package/dist/tools/findCocExtensions.js.map +1 -0
  607. package/dist/tools/findEventHandlers.d.ts +21 -0
  608. package/dist/tools/findEventHandlers.d.ts.map +1 -0
  609. package/dist/tools/findEventHandlers.js +196 -0
  610. package/dist/tools/findEventHandlers.js.map +1 -0
  611. package/dist/tools/findReferences.d.ts +30 -0
  612. package/dist/tools/findReferences.d.ts.map +1 -0
  613. package/dist/tools/findReferences.js +551 -0
  614. package/dist/tools/findReferences.js.map +1 -0
  615. package/dist/tools/formInfo.d.ts +13 -0
  616. package/dist/tools/formInfo.d.ts.map +1 -0
  617. package/dist/tools/formInfo.js +407 -0
  618. package/dist/tools/formInfo.js.map +1 -0
  619. package/dist/tools/formPattern.d.ts +19 -0
  620. package/dist/tools/formPattern.d.ts.map +1 -0
  621. package/dist/tools/formPattern.js +52 -0
  622. package/dist/tools/formPattern.js.map +1 -0
  623. package/dist/tools/generateD365Xml.d.ts +12 -0
  624. package/dist/tools/generateD365Xml.d.ts.map +1 -0
  625. package/dist/tools/generateD365Xml.js +1454 -0
  626. package/dist/tools/generateD365Xml.js.map +1 -0
  627. package/dist/tools/generateFindMethods.d.ts +69 -0
  628. package/dist/tools/generateFindMethods.d.ts.map +1 -0
  629. package/dist/tools/generateFindMethods.js +207 -0
  630. package/dist/tools/generateFindMethods.js.map +1 -0
  631. package/dist/tools/generateMetadata.d.ts +37 -0
  632. package/dist/tools/generateMetadata.d.ts.map +1 -0
  633. package/dist/tools/generateMetadata.js +213 -0
  634. package/dist/tools/generateMetadata.js.map +1 -0
  635. package/dist/tools/generateObject.d.ts +34 -0
  636. package/dist/tools/generateObject.d.ts.map +1 -0
  637. package/dist/tools/generateObject.js +47 -0
  638. package/dist/tools/generateObject.js.map +1 -0
  639. package/dist/tools/generateRelationXpp.d.ts +45 -0
  640. package/dist/tools/generateRelationXpp.d.ts.map +1 -0
  641. package/dist/tools/generateRelationXpp.js +129 -0
  642. package/dist/tools/generateRelationXpp.js.map +1 -0
  643. package/dist/tools/generateSmart.d.ts +32 -0
  644. package/dist/tools/generateSmart.d.ts.map +1 -0
  645. package/dist/tools/generateSmart.js +52 -0
  646. package/dist/tools/generateSmart.js.map +1 -0
  647. package/dist/tools/generateSmartForm.d.ts +82 -0
  648. package/dist/tools/generateSmartForm.d.ts.map +1 -0
  649. package/dist/tools/generateSmartForm.js +996 -0
  650. package/dist/tools/generateSmartForm.js.map +1 -0
  651. package/dist/tools/generateSmartReport.d.ts +90 -0
  652. package/dist/tools/generateSmartReport.d.ts.map +1 -0
  653. package/dist/tools/generateSmartReport.js +1254 -0
  654. package/dist/tools/generateSmartReport.js.map +1 -0
  655. package/dist/tools/generateSmartTable.d.ts +138 -0
  656. package/dist/tools/generateSmartTable.d.ts.map +1 -0
  657. package/dist/tools/generateSmartTable.js +1305 -0
  658. package/dist/tools/generateSmartTable.js.map +1 -0
  659. package/dist/tools/generateTableFields.d.ts +48 -0
  660. package/dist/tools/generateTableFields.d.ts.map +1 -0
  661. package/dist/tools/generateTableFields.js +186 -0
  662. package/dist/tools/generateTableFields.js.map +1 -0
  663. package/dist/tools/generateTableRelation.d.ts +35 -0
  664. package/dist/tools/generateTableRelation.d.ts.map +1 -0
  665. package/dist/tools/generateTableRelation.js +161 -0
  666. package/dist/tools/generateTableRelation.js.map +1 -0
  667. package/dist/tools/getFormPatternSpec.d.ts +13 -0
  668. package/dist/tools/getFormPatternSpec.d.ts.map +1 -0
  669. package/dist/tools/getFormPatternSpec.js +290 -0
  670. package/dist/tools/getFormPatternSpec.js.map +1 -0
  671. package/dist/tools/getFormPatterns.d.ts +68 -0
  672. package/dist/tools/getFormPatterns.d.ts.map +1 -0
  673. package/dist/tools/getFormPatterns.js +471 -0
  674. package/dist/tools/getFormPatterns.js.map +1 -0
  675. package/dist/tools/getKnowledge.d.ts +27 -0
  676. package/dist/tools/getKnowledge.d.ts.map +1 -0
  677. package/dist/tools/getKnowledge.js +50 -0
  678. package/dist/tools/getKnowledge.js.map +1 -0
  679. package/dist/tools/getLabelInfo.d.ts +21 -0
  680. package/dist/tools/getLabelInfo.d.ts.map +1 -0
  681. package/dist/tools/getLabelInfo.js +138 -0
  682. package/dist/tools/getLabelInfo.js.map +1 -0
  683. package/dist/tools/getMethod.d.ts +19 -0
  684. package/dist/tools/getMethod.d.ts.map +1 -0
  685. package/dist/tools/getMethod.js +77 -0
  686. package/dist/tools/getMethod.js.map +1 -0
  687. package/dist/tools/getMethodSource.d.ts +17 -0
  688. package/dist/tools/getMethodSource.d.ts.map +1 -0
  689. package/dist/tools/getMethodSource.js +161 -0
  690. package/dist/tools/getMethodSource.js.map +1 -0
  691. package/dist/tools/getObjectInfo.d.ts +17 -0
  692. package/dist/tools/getObjectInfo.d.ts.map +1 -0
  693. package/dist/tools/getObjectInfo.js +88 -0
  694. package/dist/tools/getObjectInfo.js.map +1 -0
  695. package/dist/tools/getTablePatterns.d.ts +45 -0
  696. package/dist/tools/getTablePatterns.d.ts.map +1 -0
  697. package/dist/tools/getTablePatterns.js +235 -0
  698. package/dist/tools/getTablePatterns.js.map +1 -0
  699. package/dist/tools/index.d.ts +11 -0
  700. package/dist/tools/index.d.ts.map +1 -0
  701. package/dist/tools/index.js +11 -0
  702. package/dist/tools/index.js.map +1 -0
  703. package/dist/tools/labels.d.ts +25 -0
  704. package/dist/tools/labels.d.ts.map +1 -0
  705. package/dist/tools/labels.js +111 -0
  706. package/dist/tools/labels.js.map +1 -0
  707. package/dist/tools/macroInfo.d.ts +23 -0
  708. package/dist/tools/macroInfo.d.ts.map +1 -0
  709. package/dist/tools/macroInfo.js +62 -0
  710. package/dist/tools/macroInfo.js.map +1 -0
  711. package/dist/tools/mapInfo.d.ts +21 -0
  712. package/dist/tools/mapInfo.d.ts.map +1 -0
  713. package/dist/tools/mapInfo.js +54 -0
  714. package/dist/tools/mapInfo.js.map +1 -0
  715. package/dist/tools/mapXml.d.ts +26 -0
  716. package/dist/tools/mapXml.d.ts.map +1 -0
  717. package/dist/tools/mapXml.js +88 -0
  718. package/dist/tools/mapXml.js.map +1 -0
  719. package/dist/tools/menuItemInfo.d.ts +20 -0
  720. package/dist/tools/menuItemInfo.d.ts.map +1 -0
  721. package/dist/tools/menuItemInfo.js +185 -0
  722. package/dist/tools/menuItemInfo.js.map +1 -0
  723. package/dist/tools/methodSignature.d.ts +33 -0
  724. package/dist/tools/methodSignature.d.ts.map +1 -0
  725. package/dist/tools/methodSignature.js +348 -0
  726. package/dist/tools/methodSignature.js.map +1 -0
  727. package/dist/tools/modifyD365File.d.ts +130 -0
  728. package/dist/tools/modifyD365File.d.ts.map +1 -0
  729. package/dist/tools/modifyD365File.js +2179 -0
  730. package/dist/tools/modifyD365File.js.map +1 -0
  731. package/dist/tools/objectInfoRegistry.d.ts +36 -0
  732. package/dist/tools/objectInfoRegistry.d.ts.map +1 -0
  733. package/dist/tools/objectInfoRegistry.js +98 -0
  734. package/dist/tools/objectInfoRegistry.js.map +1 -0
  735. package/dist/tools/objectPatterns.d.ts +16 -0
  736. package/dist/tools/objectPatterns.d.ts.map +1 -0
  737. package/dist/tools/objectPatterns.js +84 -0
  738. package/dist/tools/objectPatterns.js.map +1 -0
  739. package/dist/tools/prepare.d.ts +18 -0
  740. package/dist/tools/prepare.d.ts.map +1 -0
  741. package/dist/tools/prepare.js +47 -0
  742. package/dist/tools/prepare.js.map +1 -0
  743. package/dist/tools/prepareChange.d.ts +43 -0
  744. package/dist/tools/prepareChange.d.ts.map +1 -0
  745. package/dist/tools/prepareChange.js +327 -0
  746. package/dist/tools/prepareChange.js.map +1 -0
  747. package/dist/tools/prepareCreate.d.ts +45 -0
  748. package/dist/tools/prepareCreate.d.ts.map +1 -0
  749. package/dist/tools/prepareCreate.js +263 -0
  750. package/dist/tools/prepareCreate.js.map +1 -0
  751. package/dist/tools/queryInfo.d.ts +17 -0
  752. package/dist/tools/queryInfo.d.ts.map +1 -0
  753. package/dist/tools/queryInfo.js +46 -0
  754. package/dist/tools/queryInfo.js.map +1 -0
  755. package/dist/tools/queryViewXml.d.ts +36 -0
  756. package/dist/tools/queryViewXml.d.ts.map +1 -0
  757. package/dist/tools/queryViewXml.js +129 -0
  758. package/dist/tools/queryViewXml.js.map +1 -0
  759. package/dist/tools/renameLabel.d.ts +29 -0
  760. package/dist/tools/renameLabel.d.ts.map +1 -0
  761. package/dist/tools/renameLabel.js +377 -0
  762. package/dist/tools/renameLabel.js.map +1 -0
  763. package/dist/tools/repairFormControls.d.ts +21 -0
  764. package/dist/tools/repairFormControls.d.ts.map +1 -0
  765. package/dist/tools/repairFormControls.js +151 -0
  766. package/dist/tools/repairFormControls.js.map +1 -0
  767. package/dist/tools/reportInfo.d.ts +14 -0
  768. package/dist/tools/reportInfo.d.ts.map +1 -0
  769. package/dist/tools/reportInfo.js +302 -0
  770. package/dist/tools/reportInfo.js.map +1 -0
  771. package/dist/tools/resolveReferences.d.ts +89 -0
  772. package/dist/tools/resolveReferences.d.ts.map +1 -0
  773. package/dist/tools/resolveReferences.js +791 -0
  774. package/dist/tools/resolveReferences.js.map +1 -0
  775. package/dist/tools/reviewWorkspaceChanges.d.ts +14 -0
  776. package/dist/tools/reviewWorkspaceChanges.d.ts.map +1 -0
  777. package/dist/tools/reviewWorkspaceChanges.js +73 -0
  778. package/dist/tools/reviewWorkspaceChanges.js.map +1 -0
  779. package/dist/tools/runBpCheck.d.ts +14 -0
  780. package/dist/tools/runBpCheck.d.ts.map +1 -0
  781. package/dist/tools/runBpCheck.js +241 -0
  782. package/dist/tools/runBpCheck.js.map +1 -0
  783. package/dist/tools/search.d.ts +20 -0
  784. package/dist/tools/search.d.ts.map +1 -0
  785. package/dist/tools/search.js +246 -0
  786. package/dist/tools/search.js.map +1 -0
  787. package/dist/tools/searchLabels.d.ts +26 -0
  788. package/dist/tools/searchLabels.d.ts.map +1 -0
  789. package/dist/tools/searchLabels.js +91 -0
  790. package/dist/tools/searchLabels.js.map +1 -0
  791. package/dist/tools/searchUnified.d.ts +28 -0
  792. package/dist/tools/searchUnified.d.ts.map +1 -0
  793. package/dist/tools/searchUnified.js +48 -0
  794. package/dist/tools/searchUnified.js.map +1 -0
  795. package/dist/tools/securityArtifactInfo.d.ts +14 -0
  796. package/dist/tools/securityArtifactInfo.d.ts.map +1 -0
  797. package/dist/tools/securityArtifactInfo.js +192 -0
  798. package/dist/tools/securityArtifactInfo.js.map +1 -0
  799. package/dist/tools/securityCoverageInfo.d.ts +21 -0
  800. package/dist/tools/securityCoverageInfo.d.ts.map +1 -0
  801. package/dist/tools/securityCoverageInfo.js +248 -0
  802. package/dist/tools/securityCoverageInfo.js.map +1 -0
  803. package/dist/tools/securityInfo.d.ts +22 -0
  804. package/dist/tools/securityInfo.d.ts.map +1 -0
  805. package/dist/tools/securityInfo.js +43 -0
  806. package/dist/tools/securityInfo.js.map +1 -0
  807. package/dist/tools/securityPolicyInfo.d.ts +22 -0
  808. package/dist/tools/securityPolicyInfo.d.ts.map +1 -0
  809. package/dist/tools/securityPolicyInfo.js +49 -0
  810. package/dist/tools/securityPolicyInfo.js.map +1 -0
  811. package/dist/tools/securityPrivilegeXml.d.ts +21 -0
  812. package/dist/tools/securityPrivilegeXml.d.ts.map +1 -0
  813. package/dist/tools/securityPrivilegeXml.js +61 -0
  814. package/dist/tools/securityPrivilegeXml.js.map +1 -0
  815. package/dist/tools/serviceInfo.d.ts +25 -0
  816. package/dist/tools/serviceInfo.d.ts.map +1 -0
  817. package/dist/tools/serviceInfo.js +93 -0
  818. package/dist/tools/serviceInfo.js.map +1 -0
  819. package/dist/tools/suggestEdt.d.ts +15 -0
  820. package/dist/tools/suggestEdt.d.ts.map +1 -0
  821. package/dist/tools/suggestEdt.js +348 -0
  822. package/dist/tools/suggestEdt.js.map +1 -0
  823. package/dist/tools/suggestImplementation.d.ts +20 -0
  824. package/dist/tools/suggestImplementation.d.ts.map +1 -0
  825. package/dist/tools/suggestImplementation.js +144 -0
  826. package/dist/tools/suggestImplementation.js.map +1 -0
  827. package/dist/tools/sysTestRunner.d.ts +14 -0
  828. package/dist/tools/sysTestRunner.d.ts.map +1 -0
  829. package/dist/tools/sysTestRunner.js +145 -0
  830. package/dist/tools/sysTestRunner.js.map +1 -0
  831. package/dist/tools/tableExtensionInfo.d.ts +91 -0
  832. package/dist/tools/tableExtensionInfo.d.ts.map +1 -0
  833. package/dist/tools/tableExtensionInfo.js +305 -0
  834. package/dist/tools/tableExtensionInfo.js.map +1 -0
  835. package/dist/tools/tableInfo.d.ts +23 -0
  836. package/dist/tools/tableInfo.d.ts.map +1 -0
  837. package/dist/tools/tableInfo.js +125 -0
  838. package/dist/tools/tableInfo.js.map +1 -0
  839. package/dist/tools/toolHandler.d.ts +4 -0
  840. package/dist/tools/toolHandler.d.ts.map +1 -0
  841. package/dist/tools/toolHandler.js +508 -0
  842. package/dist/tools/toolHandler.js.map +1 -0
  843. package/dist/tools/undoLastModification.d.ts +15 -0
  844. package/dist/tools/undoLastModification.d.ts.map +1 -0
  845. package/dist/tools/undoLastModification.js +147 -0
  846. package/dist/tools/undoLastModification.js.map +1 -0
  847. package/dist/tools/updateSymbolIndex.d.ts +15 -0
  848. package/dist/tools/updateSymbolIndex.d.ts.map +1 -0
  849. package/dist/tools/updateSymbolIndex.js +520 -0
  850. package/dist/tools/updateSymbolIndex.js.map +1 -0
  851. package/dist/tools/validateCode.d.ts +24 -0
  852. package/dist/tools/validateCode.d.ts.map +1 -0
  853. package/dist/tools/validateCode.js +201 -0
  854. package/dist/tools/validateCode.js.map +1 -0
  855. package/dist/tools/validateFormPattern.d.ts +51 -0
  856. package/dist/tools/validateFormPattern.d.ts.map +1 -0
  857. package/dist/tools/validateFormPattern.js +201 -0
  858. package/dist/tools/validateFormPattern.js.map +1 -0
  859. package/dist/tools/validateObjectNaming.d.ts +21 -0
  860. package/dist/tools/validateObjectNaming.d.ts.map +1 -0
  861. package/dist/tools/validateObjectNaming.js +319 -0
  862. package/dist/tools/validateObjectNaming.js.map +1 -0
  863. package/dist/tools/validateXpp.d.ts +79 -0
  864. package/dist/tools/validateXpp.d.ts.map +1 -0
  865. package/dist/tools/validateXpp.js +705 -0
  866. package/dist/tools/validateXpp.js.map +1 -0
  867. package/dist/tools/verifyD365Project.d.ts +21 -0
  868. package/dist/tools/verifyD365Project.d.ts.map +1 -0
  869. package/dist/tools/verifyD365Project.js +299 -0
  870. package/dist/tools/verifyD365Project.js.map +1 -0
  871. package/dist/tools/viewInfo.d.ts +17 -0
  872. package/dist/tools/viewInfo.d.ts.map +1 -0
  873. package/dist/tools/viewInfo.js +46 -0
  874. package/dist/tools/viewInfo.js.map +1 -0
  875. package/dist/tools/xppKnowledge.d.ts +45 -0
  876. package/dist/tools/xppKnowledge.d.ts.map +1 -0
  877. package/dist/tools/xppKnowledge.js +2815 -0
  878. package/dist/tools/xppKnowledge.js.map +1 -0
  879. package/dist/types/context.d.ts +56 -0
  880. package/dist/types/context.d.ts.map +1 -0
  881. package/dist/types/context.js +5 -0
  882. package/dist/types/context.js.map +1 -0
  883. package/dist/utils/callDedup.d.ts +29 -0
  884. package/dist/utils/callDedup.d.ts.map +1 -0
  885. package/dist/utils/callDedup.js +87 -0
  886. package/dist/utils/callDedup.js.map +1 -0
  887. package/dist/utils/configManager.d.ts +307 -0
  888. package/dist/utils/configManager.d.ts.map +1 -0
  889. package/dist/utils/configManager.js +1169 -0
  890. package/dist/utils/configManager.js.map +1 -0
  891. package/dist/utils/d365XmlNormalizer.d.ts +9 -0
  892. package/dist/utils/d365XmlNormalizer.d.ts.map +1 -0
  893. package/dist/utils/d365XmlNormalizer.js +24 -0
  894. package/dist/utils/d365XmlNormalizer.js.map +1 -0
  895. package/dist/utils/edtExtensionValidator.d.ts +110 -0
  896. package/dist/utils/edtExtensionValidator.d.ts.map +1 -0
  897. package/dist/utils/edtExtensionValidator.js +367 -0
  898. package/dist/utils/edtExtensionValidator.js.map +1 -0
  899. package/dist/utils/eolUtils.d.ts +15 -0
  900. package/dist/utils/eolUtils.d.ts.map +1 -0
  901. package/dist/utils/eolUtils.js +21 -0
  902. package/dist/utils/eolUtils.js.map +1 -0
  903. package/dist/utils/extractManifest.d.ts +34 -0
  904. package/dist/utils/extractManifest.d.ts.map +1 -0
  905. package/dist/utils/extractManifest.js +43 -0
  906. package/dist/utils/extractManifest.js.map +1 -0
  907. package/dist/utils/fieldControlTypes.d.ts +44 -0
  908. package/dist/utils/fieldControlTypes.d.ts.map +1 -0
  909. package/dist/utils/fieldControlTypes.js +97 -0
  910. package/dist/utils/fieldControlTypes.js.map +1 -0
  911. package/dist/utils/formCloner.d.ts +77 -0
  912. package/dist/utils/formCloner.d.ts.map +1 -0
  913. package/dist/utils/formCloner.js +364 -0
  914. package/dist/utils/formCloner.js.map +1 -0
  915. package/dist/utils/formControlExpander.d.ts +68 -0
  916. package/dist/utils/formControlExpander.d.ts.map +1 -0
  917. package/dist/utils/formControlExpander.js +207 -0
  918. package/dist/utils/formControlExpander.js.map +1 -0
  919. package/dist/utils/formControlRepair.d.ts +81 -0
  920. package/dist/utils/formControlRepair.d.ts.map +1 -0
  921. package/dist/utils/formControlRepair.js +203 -0
  922. package/dist/utils/formControlRepair.js.map +1 -0
  923. package/dist/utils/formExtensionShapeValidator.d.ts +44 -0
  924. package/dist/utils/formExtensionShapeValidator.d.ts.map +1 -0
  925. package/dist/utils/formExtensionShapeValidator.js +99 -0
  926. package/dist/utils/formExtensionShapeValidator.js.map +1 -0
  927. package/dist/utils/formPatternTemplates.d.ts +77 -0
  928. package/dist/utils/formPatternTemplates.d.ts.map +1 -0
  929. package/dist/utils/formPatternTemplates.js +1917 -0
  930. package/dist/utils/formPatternTemplates.js.map +1 -0
  931. package/dist/utils/fsExtensionScanner.d.ts +76 -0
  932. package/dist/utils/fsExtensionScanner.d.ts.map +1 -0
  933. package/dist/utils/fsExtensionScanner.js +271 -0
  934. package/dist/utils/fsExtensionScanner.js.map +1 -0
  935. package/dist/utils/fuzzyMatching.d.ts +41 -0
  936. package/dist/utils/fuzzyMatching.d.ts.map +1 -0
  937. package/dist/utils/fuzzyMatching.js +186 -0
  938. package/dist/utils/fuzzyMatching.js.map +1 -0
  939. package/dist/utils/indexStaleness.d.ts +30 -0
  940. package/dist/utils/indexStaleness.d.ts.map +1 -0
  941. package/dist/utils/indexStaleness.js +104 -0
  942. package/dist/utils/indexStaleness.js.map +1 -0
  943. package/dist/utils/loadEnv.d.ts +32 -0
  944. package/dist/utils/loadEnv.d.ts.map +1 -0
  945. package/dist/utils/loadEnv.js +83 -0
  946. package/dist/utils/loadEnv.js.map +1 -0
  947. package/dist/utils/logger.d.ts +4 -0
  948. package/dist/utils/logger.d.ts.map +1 -0
  949. package/dist/utils/logger.js +11 -0
  950. package/dist/utils/logger.js.map +1 -0
  951. package/dist/utils/metadataResolver.d.ts +155 -0
  952. package/dist/utils/metadataResolver.d.ts.map +1 -0
  953. package/dist/utils/metadataResolver.js +277 -0
  954. package/dist/utils/metadataResolver.js.map +1 -0
  955. package/dist/utils/modelClassifier.d.ts +180 -0
  956. package/dist/utils/modelClassifier.d.ts.map +1 -0
  957. package/dist/utils/modelClassifier.js +373 -0
  958. package/dist/utils/modelClassifier.js.map +1 -0
  959. package/dist/utils/operationLocks.d.ts +26 -0
  960. package/dist/utils/operationLocks.d.ts.map +1 -0
  961. package/dist/utils/operationLocks.js +182 -0
  962. package/dist/utils/operationLocks.js.map +1 -0
  963. package/dist/utils/packageResolver.d.ts +52 -0
  964. package/dist/utils/packageResolver.d.ts.map +1 -0
  965. package/dist/utils/packageResolver.js +174 -0
  966. package/dist/utils/packageResolver.js.map +1 -0
  967. package/dist/utils/pathContainment.d.ts +60 -0
  968. package/dist/utils/pathContainment.d.ts.map +1 -0
  969. package/dist/utils/pathContainment.js +270 -0
  970. package/dist/utils/pathContainment.js.map +1 -0
  971. package/dist/utils/projectUtils.d.ts +14 -0
  972. package/dist/utils/projectUtils.d.ts.map +1 -0
  973. package/dist/utils/projectUtils.js +48 -0
  974. package/dist/utils/projectUtils.js.map +1 -0
  975. package/dist/utils/provenanceStore.d.ts +54 -0
  976. package/dist/utils/provenanceStore.d.ts.map +1 -0
  977. package/dist/utils/provenanceStore.js +203 -0
  978. package/dist/utils/provenanceStore.js.map +1 -0
  979. package/dist/utils/richContext.d.ts +45 -0
  980. package/dist/utils/richContext.d.ts.map +1 -0
  981. package/dist/utils/richContext.js +316 -0
  982. package/dist/utils/richContext.js.map +1 -0
  983. package/dist/utils/smartXmlBuilder.d.ts +187 -0
  984. package/dist/utils/smartXmlBuilder.d.ts.map +1 -0
  985. package/dist/utils/smartXmlBuilder.js +504 -0
  986. package/dist/utils/smartXmlBuilder.js.map +1 -0
  987. package/dist/utils/stdioSessionInfo.d.ts +53 -0
  988. package/dist/utils/stdioSessionInfo.d.ts.map +1 -0
  989. package/dist/utils/stdioSessionInfo.js +47 -0
  990. package/dist/utils/stdioSessionInfo.js.map +1 -0
  991. package/dist/utils/suggestionEngine.d.ts +20 -0
  992. package/dist/utils/suggestionEngine.d.ts.map +1 -0
  993. package/dist/utils/suggestionEngine.js +145 -0
  994. package/dist/utils/suggestionEngine.js.map +1 -0
  995. package/dist/utils/symbolLookup.d.ts +60 -0
  996. package/dist/utils/symbolLookup.d.ts.map +1 -0
  997. package/dist/utils/symbolLookup.js +0 -0
  998. package/dist/utils/symbolLookup.js.map +1 -0
  999. package/dist/utils/terminalUi.d.ts +85 -0
  1000. package/dist/utils/terminalUi.d.ts.map +1 -0
  1001. package/dist/utils/terminalUi.js +182 -0
  1002. package/dist/utils/terminalUi.js.map +1 -0
  1003. package/dist/utils/toolMetrics.d.ts +32 -0
  1004. package/dist/utils/toolMetrics.d.ts.map +1 -0
  1005. package/dist/utils/toolMetrics.js +89 -0
  1006. package/dist/utils/toolMetrics.js.map +1 -0
  1007. package/dist/utils/toolProgressMessage.d.ts +8 -0
  1008. package/dist/utils/toolProgressMessage.d.ts.map +1 -0
  1009. package/dist/utils/toolProgressMessage.js +182 -0
  1010. package/dist/utils/toolProgressMessage.js.map +1 -0
  1011. package/dist/utils/workspaceDetector.d.ts +51 -0
  1012. package/dist/utils/workspaceDetector.d.ts.map +1 -0
  1013. package/dist/utils/workspaceDetector.js +328 -0
  1014. package/dist/utils/workspaceDetector.js.map +1 -0
  1015. package/dist/utils/xppConfigProvider.d.ts +48 -0
  1016. package/dist/utils/xppConfigProvider.d.ts.map +1 -0
  1017. package/dist/utils/xppConfigProvider.js +121 -0
  1018. package/dist/utils/xppConfigProvider.js.map +1 -0
  1019. package/dist/utils/xppDocGen.d.ts +39 -0
  1020. package/dist/utils/xppDocGen.d.ts.map +1 -0
  1021. package/dist/utils/xppDocGen.js +518 -0
  1022. package/dist/utils/xppDocGen.js.map +1 -0
  1023. package/dist/utils/xppFormat.d.ts +21 -0
  1024. package/dist/utils/xppFormat.d.ts.map +1 -0
  1025. package/dist/utils/xppFormat.js +102 -0
  1026. package/dist/utils/xppFormat.js.map +1 -0
  1027. package/dist/utils/xppSelectLint.d.ts +18 -0
  1028. package/dist/utils/xppSelectLint.d.ts.map +1 -0
  1029. package/dist/utils/xppSelectLint.js +80 -0
  1030. package/dist/utils/xppSelectLint.js.map +1 -0
  1031. package/dist/validation/formPatternValidator.d.ts +53 -0
  1032. package/dist/validation/formPatternValidator.d.ts.map +1 -0
  1033. package/dist/validation/formPatternValidator.js +454 -0
  1034. package/dist/validation/formPatternValidator.js.map +1 -0
  1035. package/dist/workspace/contextRanker.d.ts +58 -0
  1036. package/dist/workspace/contextRanker.d.ts.map +1 -0
  1037. package/dist/workspace/contextRanker.js +216 -0
  1038. package/dist/workspace/contextRanker.js.map +1 -0
  1039. package/dist/workspace/contextSnapshot.d.ts +68 -0
  1040. package/dist/workspace/contextSnapshot.d.ts.map +1 -0
  1041. package/dist/workspace/contextSnapshot.js +193 -0
  1042. package/dist/workspace/contextSnapshot.js.map +1 -0
  1043. package/dist/workspace/hybridSearch.d.ts +39 -0
  1044. package/dist/workspace/hybridSearch.d.ts.map +1 -0
  1045. package/dist/workspace/hybridSearch.js +96 -0
  1046. package/dist/workspace/hybridSearch.js.map +1 -0
  1047. package/dist/workspace/workspaceScanner.d.ts +95 -0
  1048. package/dist/workspace/workspaceScanner.d.ts.map +1 -0
  1049. package/dist/workspace/workspaceScanner.js +247 -0
  1050. package/dist/workspace/workspaceScanner.js.map +1 -0
  1051. package/dist/workspace/workspaceUtils.d.ts +23 -0
  1052. package/dist/workspace/workspaceUtils.d.ts.map +1 -0
  1053. package/dist/workspace/workspaceUtils.js +77 -0
  1054. package/dist/workspace/workspaceUtils.js.map +1 -0
  1055. package/package.json +94 -0
@@ -0,0 +1,2179 @@
1
+ /**
2
+ * Modify D365FO File Tool
3
+ * Edit existing D365FO XML files (AxClass, AxTable, AxForm, etc.)
4
+ * Supports atomic operations: add method, add field, modify property
5
+ */
6
+ import { z } from 'zod';
7
+ import * as fs from 'fs/promises';
8
+ import { execFile } from 'child_process';
9
+ import util from 'util';
10
+ import path from 'path';
11
+ import { parseStringPromise } from 'xml2js';
12
+ import { getConfigManager, fallbackPackagePath, extractModelFromFilePath } from '../utils/configManager.js';
13
+ import { isStandardModel, resolveObjectPrefix, applyObjectPrefix } from '../utils/modelClassifier.js';
14
+ import { PackageResolver } from '../utils/packageResolver.js';
15
+ import { resolveDbPathLocally } from '../utils/metadataResolver.js';
16
+ import { assertWritePathAllowed } from '../utils/pathContainment.js';
17
+ import { bridgeValidateAfterWrite, canBridgeModify, bridgeAddMethod, bridgeRemoveMethod, bridgeAddField, bridgeSetProperty, bridgeReplaceCode, bridgeModifyField, bridgeRenameField, bridgeRemoveField, bridgeReplaceAllFields, bridgeAddIndex, bridgeRemoveIndex, bridgeAddRelation, bridgeRemoveRelation, bridgeAddFieldGroup, bridgeRemoveFieldGroup, bridgeAddFieldToFieldGroup, bridgeAddEnumValue, bridgeModifyEnumValue, bridgeRemoveEnumValue, bridgeAddControl, bridgeAddDataSource, bridgeAddFieldModification, bridgeAddMenuItemToMenu, bridgeRefreshProvider, } from '../bridge/index.js';
18
+ import { ProjectFileManager, ProjectFileFinder } from './createD365File.js';
19
+ import { heuristicEdtBaseType } from './generateSmartTable.js';
20
+ import { normalizeD365Xml } from '../utils/d365XmlNormalizer.js';
21
+ import { enforceGrounding } from '../utils/provenanceStore.js';
22
+ import { gateOnReferenceErrors } from './resolveReferences.js';
23
+ import { checkAddControlAgainstParentPattern, isFormPatternEnforceEnabled, } from './validateFormPattern.js';
24
+ import { validateEdtExtensionChange } from '../utils/edtExtensionValidator.js';
25
+ import { lintXppSelect } from '../utils/xppSelectLint.js';
26
+ import { getRequiredParams, renderOpSpec, OP_PARAM_ALIASES } from './d365foFileOpSpecs.js';
27
+ import { lookupSymbolNocase } from '../utils/symbolLookup.js';
28
+ /**
29
+ * Decode the standard XML entities (<, >, ', ", &) and normalise
30
+ * line endings by stripping xml2js's 
 representation of carriage return.
31
+ *
32
+ * IMPORTANT: & is decoded LAST so that sequences like `"` are first
33
+ * turned into `"` and can then, if desired, be decoded to `"`, avoiding
34
+ * incorrect double-unescaping.
35
+ */
36
+ function decodeStandardXmlEntities(source) {
37
+ return source
38
+ // xml2js Builder escapes \r as 
 — strip it to normalise to LF-only line endings
39
+ .replace(/
/g, '')
40
+ .replace(/&lt;/g, '<')
41
+ .replace(/&gt;/g, '>')
42
+ .replace(/&apos;/g, "'")
43
+ .replace(/&quot;/g, '"')
44
+ .replace(/&amp;/g, '&');
45
+ }
46
+ /**
47
+ * Decode XML entities from X++ source code.
48
+ *
49
+ * X++ source should never contain entity-encoded characters — `/// <summary>`
50
+ * doc comments, generic types like `List<str>`, and comparison operators like
51
+ * `x < y` all use literal `<` and `>`. When an AI model copies code from an
52
+ * SSRS report's entity-encoded <Text> block and passes it as `methodCode`, the
53
+ * entities would otherwise survive into the CDATA section and corrupt the source.
54
+ *
55
+ * This function decodes the 5 standard XML entities so that source code always
56
+ * contains proper characters before it is stored in the XML object.
57
+ */
58
+ export function decodeXmlEntitiesFromXppSource(source) {
59
+ return decodeStandardXmlEntities(source);
60
+ }
61
+ /**
62
+ * Reject an X++ source payload that smuggles XML/CDATA structure.
63
+ *
64
+ * Method source written through the bridge is handed verbatim to the D365FO SDK
65
+ * serializer, which wraps it in `<![CDATA[ … ]]>` and emits the surrounding
66
+ * `<Method>…</Method>` markup itself. If the caller's source already contains
67
+ * the CDATA terminator `]]>` or closing metadata tags, the serializer writes
68
+ * them inside the CDATA block unchanged — producing structurally invalid XML:
69
+ * a premature/doubled `]]>` and a stray `<Method>` that drops the enclosing
70
+ * `</Method>` (exactly the corruption D365FO refuses to deserialize). The
71
+ * direct-XML replace fallback has the same exposure: a literal string replace
72
+ * that injects `]]>` into an existing CDATA block corrupts it too.
73
+ *
74
+ * This always means the AI passed a slice of the .xml file where clean X++ was
75
+ * expected. Reject it here — before it reaches disk — with an actionable
76
+ * message, rather than silently escaping and hiding the mistake.
77
+ *
78
+ * X++ legitimately uses `<`/`>` (generics, comparisons, doc comments), so we
79
+ * only flag the CDATA terminator and the specific opening/closing metadata
80
+ * tokens, never bare angle brackets.
81
+ */
82
+ export function assertCleanXppSource(source, paramName) {
83
+ if (!source)
84
+ return;
85
+ if (source.includes(']]>')) {
86
+ throw new Error(`⛔ ${paramName} contains the CDATA terminator "]]>" — that is XML, not clean X++ source.\n\n` +
87
+ `Method source is wrapped in <![CDATA[ … ]]> by the metadata serializer, so a "]]>" inside it ` +
88
+ `breaks the CDATA block and yields invalid XML (doubled "]]>" plus a dropped </Method>), which ` +
89
+ `D365FO refuses to load.\n\n` +
90
+ `Pass ONLY the X++ code — no <Source>, <![CDATA[, ]]> or </Method> markup. The tool adds the wrapping itself.`);
91
+ }
92
+ const structuralTag = source.match(/<\/(?:Source|Methods?)>|<Method>|<!\[CDATA\[/);
93
+ if (structuralTag) {
94
+ throw new Error(`⛔ ${paramName} contains the XML metadata token "${structuralTag[0]}" — that is XML, not clean X++ source.\n\n` +
95
+ `You pasted a slice of the .xml file. Pass ONLY the X++ code — the tool emits the ` +
96
+ `<Method>, <Source> and <![CDATA[ … ]]> wrapping itself.`);
97
+ }
98
+ }
99
+ /** Strip X++ line/block/doc comments and string literals so their braces/parens
100
+ * don't skew structural scans. Returns a same-ish-length cleaned string. */
101
+ function stripXppCommentsAndStrings(s) {
102
+ let out = '';
103
+ let i = 0;
104
+ while (i < s.length) {
105
+ const two = s.slice(i, i + 2);
106
+ if (two === '//') { // line comment (incl. /// doc comments)
107
+ const nl = s.indexOf('\n', i);
108
+ i = nl === -1 ? s.length : nl;
109
+ continue;
110
+ }
111
+ if (two === '/*') { // block comment
112
+ const end = s.indexOf('*/', i + 2);
113
+ i = end === -1 ? s.length : end + 2;
114
+ continue;
115
+ }
116
+ if (s[i] === '"') { // string literal
117
+ i++;
118
+ while (i < s.length && s[i] !== '"') {
119
+ if (s[i] === '\\')
120
+ i++;
121
+ i++;
122
+ }
123
+ i++;
124
+ out += '""';
125
+ continue;
126
+ }
127
+ out += s[i];
128
+ i++;
129
+ }
130
+ return out;
131
+ }
132
+ /** Count top-level X++ method bodies: a `{` opened at brace-depth 0 immediately
133
+ * after a `)` (a method signature). Nested blocks (if/for/switch) are inside the
134
+ * body (depth > 0) and a class wrapper opens after an identifier, so neither is
135
+ * miscounted. */
136
+ export function countTopLevelMethodBodies(source) {
137
+ const cleaned = stripXppCommentsAndStrings(source);
138
+ let depth = 0;
139
+ let count = 0;
140
+ let prevSignificant = '';
141
+ for (const ch of cleaned) {
142
+ if (ch === '{') {
143
+ if (depth === 0 && prevSignificant === ')')
144
+ count++;
145
+ depth++;
146
+ }
147
+ else if (ch === '}') {
148
+ if (depth > 0)
149
+ depth--;
150
+ }
151
+ if (!/\s/.test(ch))
152
+ prevSignificant = ch;
153
+ }
154
+ return count;
155
+ }
156
+ /** Split a source string containing one or more top-level X++ methods into the
157
+ * individual method sources (each including any leading doc comments / attributes
158
+ * and its full body). Mirrors countTopLevelMethodBodies' brace/comment/string
159
+ * handling. Used to let add-method accept several methods in one call and add them
160
+ * one <Method> at a time. */
161
+ export function splitTopLevelMethodBodies(source) {
162
+ const s = source;
163
+ const methods = [];
164
+ let depth = 0;
165
+ let methodStart = -1;
166
+ let i = 0;
167
+ while (i < s.length) {
168
+ const ch = s[i];
169
+ // Start a method slice at the first significant char (incl. leading /// doc
170
+ // comments and [Attribute] blocks) after the previous method closed.
171
+ if (methodStart === -1 && !/\s/.test(ch))
172
+ methodStart = i;
173
+ const two = s.slice(i, i + 2);
174
+ if (two === '//') {
175
+ const nl = s.indexOf('\n', i);
176
+ i = nl === -1 ? s.length : nl;
177
+ continue;
178
+ }
179
+ if (two === '/*') {
180
+ const end = s.indexOf('*/', i + 2);
181
+ i = end === -1 ? s.length : end + 2;
182
+ continue;
183
+ }
184
+ if (ch === '"') {
185
+ i++;
186
+ while (i < s.length && s[i] !== '"') {
187
+ if (s[i] === '\\')
188
+ i++;
189
+ i++;
190
+ }
191
+ i++;
192
+ continue;
193
+ }
194
+ if (ch === '{') {
195
+ depth++;
196
+ }
197
+ else if (ch === '}') {
198
+ if (depth > 0)
199
+ depth--;
200
+ if (depth === 0 && methodStart !== -1) {
201
+ methods.push(s.slice(methodStart, i + 1).trim());
202
+ methodStart = -1;
203
+ }
204
+ }
205
+ i++;
206
+ }
207
+ // Trailing brace-less content (e.g. an interface/abstract method declaration).
208
+ if (methodStart !== -1) {
209
+ const tail = s.slice(methodStart).trim();
210
+ if (tail)
211
+ methods.push(tail);
212
+ }
213
+ return methods.filter(Boolean);
214
+ }
215
+ /**
216
+ * Reject an add-method payload that contains more than one method. Each add-method
217
+ * call emits a single <Method>; passing two methods drops the second outside the
218
+ * class scope and yields invalid X++ ("Unexpected token 'public' specified outside
219
+ * the scope of any class or model element"). Splitting into separate calls is the fix.
220
+ */
221
+ export function assertSingleMethodSource(source) {
222
+ if (!source)
223
+ return;
224
+ const count = countTopLevelMethodBodies(source);
225
+ if (count > 1) {
226
+ throw new Error(`⛔ add-method expects exactly ONE method, but ${count} method bodies were detected in the source.\n\n` +
227
+ `Each add-method call adds a single <Method> element. Passing multiple methods puts all but ` +
228
+ `the first OUTSIDE the class scope, producing invalid X++ ("Unexpected token 'public' specified ` +
229
+ `outside the scope of any class or model element").\n\n` +
230
+ `Add each method with its own add-method call — one method per call.`);
231
+ }
232
+ }
233
+ /**
234
+ * Derive the method name from a full X++ method source: the identifier immediately
235
+ * before the first '(' of the signature, after stripping comments, strings and
236
+ * attribute blocks (e.g. [ExtensionOf(...)]). Lets add-method callers omit methodName
237
+ * when they already pass the complete source (e.g. "public static X find(...)").
238
+ * Returns null when no signature can be found.
239
+ */
240
+ export function extractMethodNameFromSource(source) {
241
+ if (!source)
242
+ return null;
243
+ const cleaned = stripXppCommentsAndStrings(source).replace(/\[[^\]]*\]/g, ' ');
244
+ const m = cleaned.match(/\b([A-Za-z_]\w*)\s*\(/);
245
+ return m ? m[1] : null;
246
+ }
247
+ /**
248
+ * Direct XML file-level replace-code fallback, used when the C# bridge can't
249
+ * reach a method (e.g. form/form-extension control overrides not exposed via
250
+ * the Methods API). Last resort — the bridge is always preferred.
251
+ */
252
+ async function directXmlReplaceCode(filePath, oldCode, newCode) {
253
+ try {
254
+ // Files on disk are CRLF; oldCode from get_method_source is typically LF-only.
255
+ // Normalize both to LF for matching, then normalizeD365Xml restores CRLF on write.
256
+ const rawContent = await fs.readFile(filePath, 'utf-8');
257
+ const content = rawContent.replace(/^/, '').replace(/\r\n/g, '\n');
258
+ const normOld = oldCode.replace(/\r\n/g, '\n');
259
+ const normNew = newCode.replace(/\r\n/g, '\n');
260
+ if (!content.includes(normOld)) {
261
+ return null; // oldCode not found in file at all
262
+ }
263
+ // Without /g, String.replace() only replaces the first occurrence, so require
264
+ // exactly one match to avoid silently picking the wrong one.
265
+ const occurrences = content.split(normOld).length - 1;
266
+ if (occurrences > 1) {
267
+ return {
268
+ success: false,
269
+ message: `❌ directXmlReplaceCode: oldCode appears ${occurrences} times in ${filePath} — replacement is ambiguous. Provide a more specific oldCode snippet.`,
270
+ };
271
+ }
272
+ const updated = content.replace(normOld, normNew);
273
+ if (updated === content) {
274
+ return null; // no change made
275
+ }
276
+ await fs.writeFile(filePath, normalizeD365Xml(updated), 'utf-8');
277
+ console.error(`[modify_d365fo_file] ✅ directXmlReplaceCode fallback: replaced in ${filePath}`);
278
+ return {
279
+ success: true,
280
+ message: `✅ Code replaced via direct XML fallback (bridge was unavailable). File: ${filePath}`,
281
+ };
282
+ }
283
+ catch (err) {
284
+ console.error(`[modify_d365fo_file] directXmlReplaceCode failed: ${err}`);
285
+ return null;
286
+ }
287
+ }
288
+ /**
289
+ * Direct XML fallback for modify-property, used when the bridge rejects
290
+ * modify-property for an object type (e.g. AxForm) even though the property
291
+ * is a plain text element (e.g. <Caption>) editable by string replacement.
292
+ * Only used when the bridge itself reports failure.
293
+ *
294
+ * `propertyPath` is a bare element name (no XPath/dotted-path support).
295
+ * Refuses to act if the element is missing (returns null so the caller
296
+ * surfaces the original bridge error) or ambiguous (appears more than once).
297
+ */
298
+ async function directXmlModifyProperty(filePath, propertyPath, propertyValue) {
299
+ try {
300
+ const rawContent = await fs.readFile(filePath, 'utf-8');
301
+ const content = rawContent.replace(/^/, '');
302
+ const escapedValue = String(propertyValue)
303
+ .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
304
+ const tagName = propertyPath.split(/[./]/).pop();
305
+ const openTagRe = new RegExp(`<${tagName}\\b[^>]*>[\\s\\S]*?</${tagName}>`, 'g');
306
+ const selfClosingRe = new RegExp(`<${tagName}\\b([^>]*)/>`, 'g');
307
+ const openMatches = content.match(openTagRe) ?? [];
308
+ const selfClosingMatches = content.match(selfClosingRe) ?? [];
309
+ const totalMatches = openMatches.length + selfClosingMatches.length;
310
+ if (totalMatches === 0) {
311
+ return null; // property element not found — let the caller surface the original bridge error
312
+ }
313
+ if (totalMatches > 1) {
314
+ return {
315
+ success: false,
316
+ message: `❌ directXmlModifyProperty: <${tagName}> appears ${totalMatches} times in ${filePath} — ambiguous, refusing to guess which one.`,
317
+ };
318
+ }
319
+ const updated = openMatches.length === 1
320
+ ? content.replace(openTagRe, m => m.replace(/>[\s\S]*?</, `>${escapedValue}<`))
321
+ : content.replace(selfClosingRe, (_m, attrs) => `<${tagName}${attrs}>${escapedValue}</${tagName}>`);
322
+ await fs.writeFile(filePath, normalizeD365Xml(updated), 'utf-8');
323
+ console.error(`[modify_d365fo_file] ✅ directXmlModifyProperty fallback: set <${tagName}> in ${filePath}`);
324
+ return {
325
+ success: true,
326
+ message: `✅ Property '${propertyPath}' set via direct XML fallback (bridge does not support modify-property for this object type). File: ${filePath}`,
327
+ };
328
+ }
329
+ catch (err) {
330
+ console.error(`[modify_d365fo_file] directXmlModifyProperty failed: ${err}`);
331
+ return null;
332
+ }
333
+ }
334
+ /**
335
+ * Direct XML fallback for add-menu-item-to-menu.
336
+ * The C# bridge can only modify menus it has loaded from its startup roots;
337
+ * newly created menus trigger a NullRef because they aren't in the bridge's
338
+ * in-memory model yet (even after update_symbol_index). This function edits
339
+ * the XML file directly as a last-resort fallback.
340
+ */
341
+ async function directXmlAddMenuItemToMenu(filePath, menuItemToAdd, menuItemToAddType) {
342
+ try {
343
+ const rawContent = await fs.readFile(filePath, 'utf-8');
344
+ const content = rawContent.replace(/^/, '').replace(/\r\n/g, '\n');
345
+ const typeMap = { display: 'Display', action: 'Action', output: 'Output' };
346
+ const menuItemType = typeMap[menuItemToAddType?.toLowerCase()] ?? 'Display';
347
+ const newElement = `\t\t<AxMenuFunctionItem>\n` +
348
+ `\t\t\t<Name>${menuItemToAdd}</Name>\n` +
349
+ `\t\t\t<MenuItemName>${menuItemToAdd}</MenuItemName>\n` +
350
+ `\t\t\t<MenuItemType>${menuItemType}</MenuItemType>\n` +
351
+ `\t\t</AxMenuFunctionItem>`;
352
+ let updated;
353
+ if (content.includes('<Elements />')) {
354
+ updated = content.replace('<Elements />', `<Elements>\n${newElement}\n\t</Elements>`);
355
+ }
356
+ else if (content.includes('</Elements>')) {
357
+ updated = content.replace('</Elements>', `${newElement}\n\t</Elements>`);
358
+ }
359
+ else {
360
+ return null;
361
+ }
362
+ if (updated === content)
363
+ return null;
364
+ await fs.writeFile(filePath, normalizeD365Xml(updated), 'utf-8');
365
+ console.error(`[modify_d365fo_file] ✅ directXmlAddMenuItemToMenu: added '${menuItemToAdd}' to ${filePath}`);
366
+ return {
367
+ success: true,
368
+ message: `✅ Menu item '${menuItemToAdd}' (${menuItemType}) added via direct XML fallback. File: ${filePath}`,
369
+ };
370
+ }
371
+ catch (err) {
372
+ console.error(`[modify_d365fo_file] directXmlAddMenuItemToMenu failed: ${err}`);
373
+ return null;
374
+ }
375
+ }
376
+ /**
377
+ * controlType (as passed to add-control) → the form control element emitted inside
378
+ * a form-extension's <FormControl>, together with its <Type> value. Verified against
379
+ * shipped standard form extensions (e.g. InventItemSampling.AdvancedQualityManagement).
380
+ * NOTE: an integer control is `AxFormIntegerControl` (Type=Integer) — NOT
381
+ * `AxFormIntControl`. Unknown types fall back to String, which is always valid.
382
+ */
383
+ const CONTROL_TYPE_TO_FORM_CONTROL = {
384
+ string: { iType: 'AxFormStringControl', typeValue: 'String' },
385
+ integer: { iType: 'AxFormIntegerControl', typeValue: 'Integer' },
386
+ int: { iType: 'AxFormIntegerControl', typeValue: 'Integer' },
387
+ int64: { iType: 'AxFormInt64Control', typeValue: 'Int64' },
388
+ real: { iType: 'AxFormRealControl', typeValue: 'Real' },
389
+ date: { iType: 'AxFormDateControl', typeValue: 'Date' },
390
+ datetime: { iType: 'AxFormDateTimeControl', typeValue: 'DateTime' },
391
+ utcdatetime: { iType: 'AxFormDateTimeControl', typeValue: 'DateTime' },
392
+ time: { iType: 'AxFormTimeControl', typeValue: 'Time' },
393
+ guid: { iType: 'AxFormGuidControl', typeValue: 'Guid' },
394
+ checkbox: { iType: 'AxFormCheckBoxControl', typeValue: 'CheckBox' },
395
+ combobox: { iType: 'AxFormComboBoxControl', typeValue: 'ComboBox' },
396
+ button: { iType: 'AxFormButtonControl', typeValue: 'Button' },
397
+ group: { iType: 'AxFormGroupControl', typeValue: 'Group' },
398
+ };
399
+ const DEFAULT_FORM_CONTROL = { iType: 'AxFormStringControl', typeValue: 'String' };
400
+ /** Random 9-char lowercase-alphanumeric suffix, matching the SDK's
401
+ * `FormExtensionControl<rand>` wrapper-name convention (e.g. "fh5riowy1"). */
402
+ function formExtensionControlName() {
403
+ let s = '';
404
+ while (s.length < 9)
405
+ s += Math.random().toString(36).slice(2);
406
+ return `FormExtensionControl${s.slice(0, 9)}`;
407
+ }
408
+ /**
409
+ * Direct XML fallback for add-control on a form-extension.
410
+ *
411
+ * The C# bridge's AddControl resolves its target via _provider.Forms.Read(name),
412
+ * which can NEVER find a form EXTENSION (named "BaseForm.Suffix") — it always
413
+ * reports 'Form "<ext>" not found'. add-control on a form-extension therefore has
414
+ * no working bridge path at all (independent of metadata-root freshness). This
415
+ * writes an <AxFormExtensionControl> element straight into the extension's
416
+ * <Controls> collection, in the exact shape the D365FO SDK serializes (verified
417
+ * against shipped standard extensions): an empty-namespace <FormControl i:type="…">
418
+ * wrapped by <AxFormExtensionControl xmlns=""> with a <Parent> reference. It edits
419
+ * the file on disk, so it is unaffected by what the bridge has loaded.
420
+ */
421
+ async function directXmlAddControl(filePath, controlName, parentControl, controlType, dataSource, dataField, label) {
422
+ try {
423
+ const rawContent = await fs.readFile(filePath, 'utf-8');
424
+ const content = rawContent.replace(/^/, '').replace(/\r\n/g, '\n');
425
+ // Idempotency: a control with this Name already present → skip.
426
+ // (controlName is a D365 identifier, so a literal substring match is safe.)
427
+ if (content.includes(`<Name>${controlName}</Name>`)) {
428
+ return {
429
+ success: true,
430
+ message: `✅ Control '${controlName}' already present in ${filePath} — skipped (idempotent).`,
431
+ };
432
+ }
433
+ const { iType, typeValue } = CONTROL_TYPE_TO_FORM_CONTROL[(controlType || 'String').toLowerCase()] ?? DEFAULT_FORM_CONTROL;
434
+ // Inner <FormControl> children, in the order shipped extensions serialize them:
435
+ // Name → Type → FormControlExtension(nil) → DataField → DataSource → Label → [Items].
436
+ const inner = [
437
+ `\t\t\t\t<Name>${controlName}</Name>`,
438
+ `\t\t\t\t<Type>${typeValue}</Type>`,
439
+ `\t\t\t\t<FormControlExtension i:nil="true" />`,
440
+ ];
441
+ if (dataField)
442
+ inner.push(`\t\t\t\t<DataField>${dataField}</DataField>`);
443
+ if (dataSource)
444
+ inner.push(`\t\t\t\t<DataSource>${dataSource}</DataSource>`);
445
+ if (label)
446
+ inner.push(`\t\t\t\t<Label>${label}</Label>`);
447
+ if (typeValue === 'ComboBox')
448
+ inner.push(`\t\t\t\t<Items />`);
449
+ const newElement = `\t\t<AxFormExtensionControl xmlns="">\n` +
450
+ `\t\t\t<Name>${formExtensionControlName()}</Name>\n` +
451
+ `\t\t\t<FormControl xmlns="" i:type="${iType}">\n` +
452
+ inner.join('\n') + '\n' +
453
+ `\t\t\t</FormControl>\n` +
454
+ `\t\t\t<Parent>${parentControl}</Parent>\n` +
455
+ `\t\t</AxFormExtensionControl>`;
456
+ let updated;
457
+ if (content.includes('<Controls />')) {
458
+ updated = content.replace('<Controls />', `<Controls>\n${newElement}\n\t</Controls>`);
459
+ }
460
+ else if (content.includes('</Controls>')) {
461
+ updated = content.replace('</Controls>', `${newElement}\n\t</Controls>`);
462
+ }
463
+ else {
464
+ return null; // no <Controls> collection — not a form-extension shape we recognise
465
+ }
466
+ if (updated === content)
467
+ return null;
468
+ await fs.writeFile(filePath, normalizeD365Xml(updated), 'utf-8');
469
+ console.error(`[modify_d365fo_file] ✅ directXmlAddControl: added '${controlName}' (${iType}) to ${filePath}`);
470
+ return {
471
+ success: true,
472
+ message: `✅ Control '${controlName}' (${iType}) added to '${parentControl}' via direct XML fallback. File: ${filePath}`,
473
+ };
474
+ }
475
+ catch (err) {
476
+ console.error(`[modify_d365fo_file] directXmlAddControl failed: ${err}`);
477
+ return null;
478
+ }
479
+ }
480
+ /**
481
+ * Heuristic: does a bridge failure message indicate the C# provider could not
482
+ * resolve the target object (vs. a genuine operation error like "index already
483
+ * exists")? An unresolved object is the one failure worth a refresh+retry,
484
+ * because an object created this session may not be in the provider's
485
+ * startup-fixed metadata roots yet.
486
+ */
487
+ export function isUnresolvedObjectError(message) {
488
+ if (!message)
489
+ return false;
490
+ // Content/operation failures that merely contain "not found" are NOT object
491
+ // resolution and must not trigger the refresh+retry or the "could not resolve"
492
+ // guidance: replace-code's "oldCode not found in <obj>.<method>", a missing
493
+ // method/field/index, etc. The object was resolved — only the snippet/member wasn't.
494
+ if (/\b(oldcode|new ?code|code|snippet|method|field|index|relation|element)\b[^.]*\bnot found/i.test(message)) {
495
+ return false;
496
+ }
497
+ // Genuine object-resolution failures: a quoted object name "'X' not found",
498
+ // or the explicit resolve/model phrases the bridge emits.
499
+ return /'[^']+'\s+not found|could not resolve|does not exist|not in (the )?(metadata )?model|cannot determine model/i.test(message);
500
+ }
501
+ /**
502
+ * Build the actionable "object could not be resolved" error for a modify
503
+ * operation. When `bridgeReported` is supplied it is included verbatim so the
504
+ * caller sees exactly what the C# bridge said rather than a generic guess.
505
+ */
506
+ function unresolvedObjectError(operation, objectType, objectName, actualFilePath, bridgeReported) {
507
+ return (`Bridge operation '${operation}' could not resolve ${objectType} '${objectName}'.\n` +
508
+ (bridgeReported ? `Bridge reported: ${bridgeReported}\n` : '') +
509
+ `Most likely cause: the C# metadata bridge could not find '${objectName}' in its metadata model.\n` +
510
+ `This is typical right after creating an object in the same session — the bridge's metadata ` +
511
+ `roots are fixed at startup, so a file written this session may not be in its model.\n` +
512
+ `Auto-refresh was already attempted once and still failed.\n\n` +
513
+ `Reliable fallback (works for tables, forms, classes created this session):\n` +
514
+ ` Read ${objectName}.xml with get_object_info, add the ${operation.replace('add-', '')} ` +
515
+ `element directly to the XML, then re-write the whole file:\n` +
516
+ ` d365fo_file(action="create", overwrite=true, xmlContent="<complete updated XML>")\n\n` +
517
+ `Alternative — try in order:\n` +
518
+ ` 1. update_symbol_index({ filePath: "<path to ${objectName}.xml>" })\n` +
519
+ ` 2. Check D365FO_CUSTOM_PACKAGES_PATH points to the correct metadata folder.\n` +
520
+ ` 3. Confirm ${objectName}.xml actually exists on disk.\n` +
521
+ (actualFilePath ? `Resolved file path: ${actualFilePath}\n` : '') +
522
+ `If none of these apply, the object may simply not exist or its name may be misspelled.`);
523
+ }
524
+ const ModifyD365FileArgsSchema = z.object({
525
+ objectType: z.enum([
526
+ 'class', 'table', 'form', 'enum', 'query', 'view', 'edt', 'data-entity', 'report',
527
+ 'table-extension', 'class-extension', 'form-extension', 'enum-extension', 'edt-extension',
528
+ 'data-entity-extension',
529
+ 'menu-item-display', 'menu-item-action', 'menu-item-output',
530
+ 'menu-item-display-extension', 'menu-item-action-extension', 'menu-item-output-extension',
531
+ 'menu', 'menu-extension',
532
+ 'security-privilege', 'security-duty', 'security-role',
533
+ ]).describe('Type of D365FO object'),
534
+ objectName: z.string().optional().describe('Name of the object to modify. Optional when filePath is provided — it is then ' +
535
+ 'derived from the file basename (the bridge resolves objects by name, which the ' +
536
+ 'file path already determines).'),
537
+ operation: z.enum([
538
+ 'add-method', 'remove-method', 'replace-code',
539
+ 'add-field', 'modify-field', 'rename-field', 'replace-all-fields', 'remove-field',
540
+ 'add-index', 'remove-index',
541
+ 'add-relation', 'remove-relation',
542
+ 'add-field-group', 'remove-field-group', 'add-field-to-field-group',
543
+ 'add-field-modification',
544
+ 'add-data-source',
545
+ 'modify-property',
546
+ 'add-control',
547
+ 'add-enum-value', 'modify-enum-value', 'remove-enum-value',
548
+ 'add-display-method', 'add-table-method', 'add-menu-item-to-menu',
549
+ ]).describe('Operation to perform. ' +
550
+ 'replace-code REQUIRES parameters: oldCode (exact code to find) + newCode (replacement). ' +
551
+ 'add-method REQUIRES: methodName + sourceCode. ' +
552
+ 'add-table-method: pass tableMethodType (find/exist/findByRecId/validateWrite/validateDelete/initValue) ' +
553
+ '(+ tableKeyField for find/exist) to auto-generate the method, OR methodName + sourceCode for a custom one. ' +
554
+ 'add-display-method: pass methodName + displayMethodReturnEdt to auto-generate a stub, OR methodName + sourceCode. ' +
555
+ 'For form control override methods with replace-code, use methodName="ControlName.methodName" (e.g. "PostButton.clicked").'),
556
+ // For add-enum-value / modify-enum-value / remove-enum-value
557
+ enumValueName: z.string().optional().describe('Enum value name for add-enum-value / modify-enum-value / remove-enum-value. ' +
558
+ 'E.g. "Approved", "Pending", "Rejected". For modify-enum-value this is the EXISTING ' +
559
+ 'name used to locate the value — see enumValueNewName to rename it.'),
560
+ enumValueNewName: z.string().optional().describe('modify-enum-value ONLY: renames the enum value (its Name/identifier) from ' +
561
+ 'enumValueName to this. Numeric value and label are unaffected unless ' +
562
+ 'enumValueInt/enumValueLabel are also given.'),
563
+ enumValueLabel: z.string().optional().describe('Label reference for the enum value (e.g. "@MyModel:Approved"). ' +
564
+ 'Used with add-enum-value and modify-enum-value.'),
565
+ enumValueHelpText: z.string().optional().describe('Help text reference for the enum value (e.g. "@MyModel:ApprovedHelp"). Optional.'),
566
+ enumValueInt: z.number().optional().describe('Explicit integer value for the enum value. ' +
567
+ 'If omitted for add-enum-value, the next available value is assigned automatically. ' +
568
+ 'Use with modify-enum-value to change the integer value (rare — may break existing data).'),
569
+ enumValueCountryRegionCodes: z.string().optional().describe('ISO country/region codes for the enum value, comma-separated (e.g. "CZ", "CZ,SK"). ' +
570
+ 'Used with add-enum-value to restrict the value to specific locales.'),
571
+ // For add-display-method
572
+ displayMethodReturnEdt: z.string().optional().describe('EDT or type name the display method returns, e.g. "Name", "AmountMST", "SalesStatus". ' +
573
+ 'Used with add-display-method to set the return type automatically.'),
574
+ // For add-table-method
575
+ tableMethodType: z.enum(['find', 'exist', 'findByRecId', 'validateWrite', 'validateDelete', 'initValue']).optional().describe('Standard table method pattern to generate. Used with add-table-method. ' +
576
+ 'find: returns a single record by key field. exist: returns true/false. ' +
577
+ 'findByRecId: returns record by RecId. validateWrite/validateDelete/initValue: standard overrides.'),
578
+ tableKeyField: z.string().optional().describe('Name of the primary key field for find/exist patterns (e.g. "ItemId", "SalesId"). ' +
579
+ 'Used with add-table-method when tableMethodType is find or exist.'),
580
+ // For add-menu-item-to-menu
581
+ menuItemToAdd: z.string().optional().describe('Name of the menu item to add (e.g. "MyCustomForm"). Used with add-menu-item-to-menu.'),
582
+ menuItemToAddType: z.enum(['display', 'action', 'output']).optional().describe('Type of menu item to add: display (form), action (class), output (report). ' +
583
+ 'Used with add-menu-item-to-menu. Defaults to display.'),
584
+ // For add-control (form-extension only)
585
+ controlName: z.string().optional().describe('Name of the new form control to add inside the form extension. ' +
586
+ 'e.g. "MyCustPriorityTier". Used as <Name> inside <FormControl>.'),
587
+ parentControl: z.string().optional().describe('Name of the existing parent control/tab/group in the base form to insert into. ' +
588
+ 'e.g. "TabGeneral", "HeaderGroup", "TabPageSales". ' +
589
+ 'Becomes the <Parent> element of the AxFormExtensionControl wrapper.'),
590
+ controlDataSource: z.string().optional().describe('Data source name for the new control binding (e.g. "CustTable"). ' +
591
+ 'Required when controlDataField is provided.'),
592
+ controlDataField: z.string().optional().describe('Data field name for the new control binding (e.g. "MyCustPriorityTier"). ' +
593
+ 'The field must already exist in the table (extension) before adding the UI control.'),
594
+ controlType: z.string().optional().describe('Form control type (default: String). Determines i:type and <Type> in the XML. ' +
595
+ 'Supported values: String, Integer, Real, CheckBox, ComboBox, Date, DateTime, Int64, Group, Button, CommandButton, MenuFunctionButton. ' +
596
+ 'Use CheckBox for NoYes/boolean fields. Use ComboBox for enum fields. ' +
597
+ 'If omitted the tool auto-picks based on the EDT base type if controlDataField is provided.'),
598
+ controlLabel: z.string().optional().describe('Optional label for the new control (add-control). Becomes the control <Label>.'),
599
+ positionType: z.string().optional().describe('Optional positioning: AfterItem | BeforeItem. Omit to append at the end of the parent.'),
600
+ previousSibling: z.string().optional().describe('Name of the sibling control to position after (used with positionType=AfterItem).'),
601
+ baseFormName: z.string().optional().describe('Base form name used for auto-resolving parentControl when the extension name does not contain it. ' +
602
+ 'E.g. if objectName="SalesOrder.MyExt" the base form is auto-detected as "SalesOrder". ' +
603
+ 'Pass this only when auto-detection fails (e.g. the extension has a non-standard name).'),
604
+ // For add-method
605
+ methodName: z.string().optional().describe('Name of method to add/remove'),
606
+ methodCode: z.string().optional().describe('X++ code for the method — either the FULL source (access modifiers + return type + name + params + body) ' +
607
+ 'or just the method body. When the full source is provided (first real code line contains an access ' +
608
+ 'modifier and the method name followed by "("), it is used as-is. When only a body is provided, ' +
609
+ 'the signature is assembled from methodModifiers, methodReturnType, methodName, and methodParameters. ' +
610
+ 'Alias: sourceCode (preferred when passing a complete CoC skeleton or full method source).'),
611
+ sourceCode: z.string().optional().describe('Alias for methodCode — pass the FULL X++ method source including access modifiers, return type, ' +
612
+ 'method name, parameters, attributes (e.g. [ExtensionOf(...)]), and body. ' +
613
+ 'This is the preferred parameter when passing a complete CoC skeleton. ' +
614
+ 'Either methodCode or sourceCode may be used; sourceCode takes precedence if both are supplied.'),
615
+ // For replace-code (REQUIRED for operation="replace-code" — do NOT use sourceCode for this)
616
+ oldCode: z.string().optional().describe('REQUIRED for replace-code. Exact existing X++ code snippet to find and replace. ' +
617
+ 'Must match the source text exactly (leading/trailing whitespace is trimmed for matching). ' +
618
+ 'If methodName is also provided the search is scoped to that method\'s Source block only. ' +
619
+ 'For form control override methods, use methodName="ControlName.methodName" (e.g. "PostButton.clicked").'),
620
+ newCode: z.string().optional().describe('REQUIRED for replace-code. Replacement X++ code snippet. ' +
621
+ 'Replaces the first occurrence of oldCode in the target source block. ' +
622
+ 'Pass empty string "" to delete the matched oldCode snippet.'),
623
+ methodModifiers: z.string().optional().describe('Method modifiers (e.g., "public static")'),
624
+ methodReturnType: z.string().optional().describe('Return type of method'),
625
+ methodParameters: z.string().optional().describe('Method parameters (e.g., "str _param1, int _param2")'),
626
+ // For add-field / modify-field (tables)
627
+ fieldName: z.string().optional().describe('Name of field to add/remove/modify/rename'),
628
+ fieldNewName: z.string().optional().describe('New name for the field (required for rename-field operation)'),
629
+ fieldType: z.string().optional().describe('EDT name for the field (for add-field: required — pass the EDT name, e.g. "InventQty", "WHSZoneId"). For modify-field: new EDT to set.'),
630
+ fieldBaseType: z.string().optional().describe('Base type that determines the XML element for add-field: String | Integer | Real | Date | DateTime | Int64 | GUID | Enum. ' +
631
+ 'REQUIRED when fieldType is an EDT — pass the EDT base type so the correct AxTableFieldReal/AxTableFieldDate/… is used. ' +
632
+ 'Examples: fieldType="InventQty" fieldBaseType="Real"; fieldType="TransDate" fieldBaseType="Date"; fieldType="ItemId" fieldBaseType="String". ' +
633
+ 'Without this, all EDT fields default to AxTableFieldString which is WRONG for numeric/date types.'),
634
+ fieldMandatory: z.boolean().optional().describe('Is field mandatory'),
635
+ fieldLabel: z.string().optional().describe('Field label'),
636
+ fieldHelpText: z.string().optional().describe('Field help text (modify-field).'),
637
+ fieldEnumType: z.string().optional().describe('Enum name to set on the field (modify-field, for enum-typed fields).'),
638
+ fieldStringSize: z.string().optional().describe('String size to set on the field (modify-field, for string-typed fields).'),
639
+ fields: z.array(z.object({
640
+ name: z.string(),
641
+ edt: z.string().optional(),
642
+ type: z.string().optional().describe('Base type for the XML element: String|Real|Integer|Date|DateTime|Int64|GUID|Enum. REQUIRED when edt is an EDT name — without it defaults to AxTableFieldString!'),
643
+ mandatory: z.boolean().optional(),
644
+ label: z.string().optional(),
645
+ })).optional().describe('Full list of fields for replace-all-fields operation. Each item: { name, edt?, type?, mandatory?, label? }. ' +
646
+ 'IMPORTANT: always pass type= the base type (String/Real/Integer/Date/DateTime/Int64/GUID) alongside edt= so the correct XML element is used. ' +
647
+ 'Example: { name: "TransQty", edt: "InventQty", type: "Real" }. ' +
648
+ 'All existing fields are replaced atomically.'),
649
+ // For add-index / remove-index (table, table-extension)
650
+ indexName: z.string().optional().describe('Index name for add-index / remove-index.'),
651
+ indexFields: z.array(z.object({
652
+ fieldName: z.string(),
653
+ direction: z.enum(['Asc', 'Desc']).optional(),
654
+ })).optional().describe('Fields that make up the index. Required for add-index.'),
655
+ indexAllowDuplicates: z.boolean().optional().describe('Whether index allows duplicates (default: false = unique).'),
656
+ indexAlternateKey: z.boolean().optional().describe('Whether index is an alternate key.'),
657
+ indexEnabled: z.boolean().optional().describe('Whether index is enabled (default: true).'),
658
+ // For add-relation / remove-relation (table, table-extension)
659
+ relationName: z.string().optional().describe('Relation name for add-relation / remove-relation.'),
660
+ relatedTable: z.string().optional().describe('Name of the related (foreign key) table.'),
661
+ relationConstraints: z.array(z.object({
662
+ fieldName: z.string().describe('Local field name.'),
663
+ relatedFieldName: z.string().describe('Field name in the related table.'),
664
+ })).optional().describe('Field constraints for the relation (field = relatedField pairs).'),
665
+ relationCardinality: z.string().optional().describe('Cardinality on local side: ZeroMore | ZeroOne | ExactlyOne (default: ZeroMore).'),
666
+ relatedTableCardinality: z.string().optional().describe('Cardinality on related side: ZeroMore | ZeroOne | ExactlyOne (default: ExactlyOne).'),
667
+ relationshipType: z.string().optional().describe('Relationship type: Association | Composition | Aggregation | Link | Specialization (default: Association).'),
668
+ // For add-field-group / remove-field-group / add-field-to-field-group (table, table-extension)
669
+ fieldGroupName: z.string().optional().describe('Field group name. For add-field-to-field-group in a table-extension: name of the group (new or existing base-table group).'),
670
+ fieldGroupFields: z.array(z.string()).optional().describe('Initial field names for add-field-group. Can be empty — add fields later with add-field-to-field-group.'),
671
+ fieldGroupLabel: z.string().optional().describe('Label for add-field-group (optional).'),
672
+ extendBaseFieldGroup: z.boolean().optional().describe('Only for table-extension add-field-to-field-group: when true, adds the field to <FieldGroupExtensions> ' +
673
+ '(extending an existing base-table field group). When false/omitted, adds to <FieldGroups> (a new group defined in the extension).'),
674
+ // For add-field-modification (table-extension only)
675
+ // uses fieldName, fieldLabel, fieldMandatory (already defined above)
676
+ // For add-data-source (form-extension)
677
+ dataSourceName: z.string().optional().describe('Data source reference name for add-data-source (e.g. "MyTable_1").'),
678
+ dataSourceTable: z.string().optional().describe('Base table name for add-data-source (e.g. "MyTable").'),
679
+ joinSource: z.string().optional().describe('Optional name of an existing data source on the form to join the new data source to (add-data-source).'),
680
+ linkType: z.string().optional().describe('Optional join/link type when joinSource is set (add-data-source): InnerJoin | OuterJoin | ExistJoin | NotExistJoin | Delayed | Active | Passive.'),
681
+ // For modify-property
682
+ propertyPath: z.string().optional().describe('Property name to set. ' +
683
+ 'For tables (AxTable): TableGroup, TitleField1, TitleField2, TableType (TempDB/RegularTable/InMemory), ' +
684
+ 'CacheLookup, ClusteredIndex, PrimaryIndex, SaveDataPerCompany, Label, HelpText, Extends, SystemTable. ' +
685
+ 'For table-extensions (AxTableExtension): properties are stored inside <PropertyModifications> as ' +
686
+ '<AxPropertyModification> entries. Supported: Label, HelpText, TableGroup, CacheLookup, TitleField1, TitleField2, ' +
687
+ 'ClusteredIndex, PrimaryIndex, SaveDataPerCompany, TableType, SystemTable, ' +
688
+ 'ModifiedDateTime (Yes/No), CreatedDateTime (Yes/No), ModifiedBy (Yes/No), CreatedBy (Yes/No), ' +
689
+ 'CountryRegionCodes (comma-separated, e.g. "CZ,SK"). ' +
690
+ 'For EDTs: Extends, StringSize, Label, HelpText, ReferenceTable, ReferenceField. ' +
691
+ 'For classes: Extends, Abstract, Final, Label. ' +
692
+ 'For nested properties use dot notation, e.g. "Fields.AxTableField.Name" (rare). ' +
693
+ 'Examples: propertyPath="TableGroup" propertyValue="Group"; propertyPath="TitleField1" propertyValue="ItemId"; ' +
694
+ 'propertyPath="TableType" propertyValue="TempDB"; propertyPath="Extends" propertyValue="WHSZoneId"; ' +
695
+ 'propertyPath="ModifiedDateTime" propertyValue="Yes" (table-extension); ' +
696
+ 'propertyPath="CountryRegionCodes" propertyValue="CZ,SK" (table-extension)'),
697
+ propertyValue: z.string().optional().describe('New property value'),
698
+ // Options
699
+ createBackup: z.boolean().optional().default(false).describe('Create a .bak backup of the file before modifying it (default: false). Changes can also be reverted with undo_last_modification (git checkout) without a backup. When the file is NOT inside a git repository, a backup is created automatically even with false, since undo_last_modification would not work there.'),
700
+ modelName: z.string().optional().describe('Model name (auto-detected if not provided). Pass this if the file was just created and is not yet indexed.'),
701
+ packageName: z.string().optional().describe('Package name. Auto-resolved if omitted.'),
702
+ packagePath: z.string().optional().describe('Root packages path to search when the object lives outside the default PackagesLocalDirectory ' +
703
+ '(e.g. a repo metadata checkout like "K:\\\\repos\\\\…\\\\metadata"). Used to locate the file for ' +
704
+ 'backup and the direct-XML fallback. NOTE: bridge-backed operations (add-method, add-field, etc.) ' +
705
+ 'resolve objects via the C# bridge, which is configured with fixed roots at startup — if the model ' +
706
+ 'is not under the bridge\'s roots, set context.customPackagesPath / D365FO_CUSTOM_PACKAGES_PATH so ' +
707
+ 'the bridge picks it up, or pass an explicit filePath.'),
708
+ workspacePath: z.string().optional().describe('Path to workspace for finding file'),
709
+ filePath: z.string().optional().describe('Absolute path to the XML file. Use this when the object was just created and the path is already known ' +
710
+ '(e.g. from d365fo_file(action="create") output). Bypasses symbol DB lookup entirely.'),
711
+ addToProject: z.boolean().optional().default(false).describe('When true, adds the modified file to the Visual Studio project (.rnrproj). ' +
712
+ 'Use this when the file exists on disk but is not yet tracked in the VS project. ' +
713
+ 'Requires projectPath or solutionPath (explicit or via .mcp.json). Default: false.'),
714
+ projectPath: z.string().optional().describe('Path to .rnrproj file. Required for addToProject to work. Auto-detected from .mcp.json if omitted.'),
715
+ solutionPath: z.string().optional().describe('Path to VS solution directory. Used to find .rnrproj when projectPath is not given.'),
716
+ groundingToken: z.string().optional().describe('Provenance token returned by prepare(mode="change"). Proves the change was grounded in the indexed codebase. ' +
717
+ 'Required for *-extension objectTypes when GROUNDING_ENFORCE=true on the server.'),
718
+ });
719
+ export async function modifyD365FileTool(request, context) {
720
+ try {
721
+ const args = ModifyD365FileArgsSchema.parse(request.params.arguments);
722
+ // Decode XML entities in X++ payloads. An AI that copied entity-encoded code (an
723
+ // SSRS <Text> block, or escaped doc comments like "/// &lt;summary&gt;") sends
724
+ // &lt;/&gt;/&amp;/&quot;/&apos;; the serializer would otherwise write them literally
725
+ // into the source. No-op for clean X++ — literal <, >, && and " are not entities.
726
+ if (args.sourceCode)
727
+ args.sourceCode = decodeXmlEntitiesFromXppSource(args.sourceCode);
728
+ if (args.methodCode)
729
+ args.methodCode = decodeXmlEntitiesFromXppSource(args.methodCode);
730
+ if (args.newCode)
731
+ args.newCode = decodeXmlEntitiesFromXppSource(args.newCode);
732
+ if (args.oldCode)
733
+ args.oldCode = decodeXmlEntitiesFromXppSource(args.oldCode);
734
+ // objectName is optional when filePath is given — derive it from the file
735
+ // basename using path.win32.basename (handles Windows backslash paths on
736
+ // all platforms — path.posix.basename treats '\\' as a regular character).
737
+ if (!args.objectName) {
738
+ if (args.filePath) {
739
+ args.objectName = path.win32.basename(args.filePath, '.xml');
740
+ }
741
+ else {
742
+ return {
743
+ content: [{
744
+ type: 'text',
745
+ text: "❌ Provide 'objectName' — or 'filePath', from which the object name is derived.",
746
+ }],
747
+ isError: true,
748
+ };
749
+ }
750
+ }
751
+ // Grounding enforcement: modifying an extension changes the behaviour of an
752
+ // existing base object — when GROUNDING_ENFORCE=true the model must prove
753
+ // (via prepare_change) that it inspected the real object first.
754
+ if (args.objectType.endsWith('-extension')) {
755
+ const groundingError = enforceGrounding(args.groundingToken, `d365fo_file(action="modify", objectType="${args.objectType}", objectName="${args.objectName}", operation="${args.operation}")`, args.objectName);
756
+ if (groundingError)
757
+ return groundingError;
758
+ }
759
+ // Semantic reference gate: when GROUNDING_ENFORCE=true, every identifier in
760
+ // X++ source about to be written must be proven against the symbol index.
761
+ const xppToWrite = args.sourceCode ?? args.methodCode ?? args.newCode;
762
+ const referenceError = gateOnReferenceErrors(xppToWrite, context.symbolIndex, `d365fo_file(action="modify", objectType="${args.objectType}", objectName="${args.objectName}", operation="${args.operation}")`);
763
+ if (referenceError)
764
+ return referenceError;
765
+ // CDATA-corruption guard: any X++ payload that will be CDATA-wrapped by the
766
+ // serializer (add-method's source) or spliced into an existing CDATA block
767
+ // (replace-code's newCode) must be clean X++ — never a slice of .xml markup.
768
+ // A "]]>" or stray <Method>/<Source> in the payload otherwise survives into
769
+ // the file and produces the invalid doubled-"]]>" / dropped-</Method> shape.
770
+ assertCleanXppSource(args.sourceCode, 'sourceCode');
771
+ assertCleanXppSource(args.methodCode, 'methodCode');
772
+ assertCleanXppSource(args.newCode, 'newCode');
773
+ // add-method emits exactly one <Method>; reject a payload carrying multiple
774
+ // methods (the extras would land outside the class → invalid X++). Only for the
775
+ // method-adding operations — replace-code's newCode is a snippet, not a method.
776
+ if (['add-method', 'add-display-method', 'add-table-method'].includes(args.operation)) {
777
+ const methodSrc = args.sourceCode ?? args.methodCode;
778
+ // add-method may carry several methods — they are split and added one <Method>
779
+ // at a time below. add-display-method / add-table-method generate a single
780
+ // method, so multiple bodies there are still a mistake.
781
+ if (args.operation !== 'add-method') {
782
+ assertSingleMethodSource(methodSrc);
783
+ }
784
+ // Derive methodName from the source signature when omitted — the full method
785
+ // source already contains the name (e.g. "public static X find(...)" → "find").
786
+ // Skip derivation when the payload holds multiple methods (handled per-method).
787
+ if (!args.methodName && countTopLevelMethodBodies(methodSrc ?? '') <= 1) {
788
+ const derived = extractMethodNameFromSource(methodSrc);
789
+ if (derived) {
790
+ args.methodName = derived;
791
+ console.error(`[modify_d365fo_file] methodName omitted — derived '${derived}' from the source signature`);
792
+ }
793
+ }
794
+ }
795
+ const { symbolIndex } = context;
796
+ let { objectType, operation, createBackup, modelName, workspacePath, filePath: explicitFilePath, } = args;
797
+ // objectName guaranteed non-null by the derivation block above.
798
+ // Declared as `let` so it can be corrected from the resolved file basename below.
799
+ let objectName = args.objectName;
800
+ // ── Auto-resolve parentControl for add-control on form-extension ─────────
801
+ // When `parentControl` is a fuzzy / lowercase string (e.g. "general"), look
802
+ // up the base form XML, walk the control tree, and resolve to the exact name.
803
+ // This makes add-control seamless — no prior get_object_info(form) call required.
804
+ let addControlNote = '';
805
+ let generationNote = '';
806
+ if (operation === 'add-control' && objectType === 'form-extension' && args.parentControl) {
807
+ const resolution = await resolveParentControl(objectName, args.parentControl, symbolIndex, args.baseFormName);
808
+ if (resolution && 'multiple' in resolution) {
809
+ const candidateList = resolution.multiple
810
+ .slice(0, 20)
811
+ .map(c => ` • \`${c.name}\`` +
812
+ (c.parentName ? ` (parent: \`${c.parentName}\`)` : '') +
813
+ ` — path: ${c.pathStr}`)
814
+ .join('\n');
815
+ return {
816
+ content: [{
817
+ type: 'text',
818
+ text: `⚠️ **Ambiguous parentControl** — "${args.parentControl}" matches multiple controls in the base form.\n\n` +
819
+ `**Candidates** (${resolution.multiple.length}):\n${candidateList}\n\n` +
820
+ `Re-call \`add-control\` with the exact \`parentControl\` name from the list above.`,
821
+ }],
822
+ isError: true,
823
+ };
824
+ }
825
+ if (resolution && 'resolved' in resolution) {
826
+ if (resolution.resolved !== args.parentControl) {
827
+ addControlNote = `\n\n> 🔍 **parentControl** auto-resolved: \`"${args.parentControl}"\` → \`"${resolution.resolved}"\` (${resolution.pathStr})`;
828
+ }
829
+ else {
830
+ addControlNote = `\n\n> ✅ **parentControl** \`"${resolution.resolved}"\` confirmed in base form (${resolution.pathStr})`;
831
+ }
832
+ args.parentControl = resolution.resolved;
833
+ }
834
+ // null → form not found or no match; proceed with original value (compiler will catch it)
835
+ }
836
+ // ── Form-pattern pre-flight for add-control ───────────────────────────────
837
+ // When the parent container declares a sub-pattern (e.g. FieldsFieldGroups),
838
+ // verify the new control's type is allowed there. Blocking when
839
+ // FORM_PATTERN_ENFORCE is enabled (default); advisory note otherwise.
840
+ if (operation === 'add-control' &&
841
+ (objectType === 'form' || objectType === 'form-extension') &&
842
+ args.parentControl &&
843
+ args.controlType) {
844
+ const baseFormName = objectType === 'form'
845
+ ? objectName
846
+ : (args.baseFormName || objectName.split('.')[0]);
847
+ const baseXml = await findBaseFormXml(baseFormName, symbolIndex);
848
+ if (baseXml) {
849
+ const verdict = await checkAddControlAgainstParentPattern(baseXml, args.parentControl, args.controlType);
850
+ if (verdict && !verdict.allowed) {
851
+ const allowedList = verdict.allowedTypes === 'any' ? 'any' : verdict.allowedTypes.join(', ');
852
+ if (isFormPatternEnforceEnabled()) {
853
+ return {
854
+ content: [{
855
+ type: 'text',
856
+ text: `⛔ add-control blocked — parent control "${args.parentControl}" follows sub-pattern ` +
857
+ `**${verdict.parentPattern}**, which does not allow a "${args.controlType}" child.\n\n` +
858
+ `Allowed control types here: ${allowedList}.\n\n` +
859
+ `Options:\n` +
860
+ ` 1. Use an allowed control type (e.g. controlType="String" for a bound field).\n` +
861
+ ` 2. Target a different parent container (use get_object_info(objectType="form", name=...) to inspect the hierarchy).\n` +
862
+ ` 3. Set FORM_PATTERN_ENFORCE=false to bypass pattern enforcement.`,
863
+ }],
864
+ isError: true,
865
+ };
866
+ }
867
+ addControlNote +=
868
+ `\n\n> ⚠️ Pattern warning: parent "${args.parentControl}" follows ${verdict.parentPattern}, ` +
869
+ `which does not allow "${args.controlType}" children (allowed: ${allowedList}). ` +
870
+ `FORM_PATTERN_ENFORCE is disabled — proceeding anyway.`;
871
+ }
872
+ }
873
+ }
874
+ // 1. Find the file
875
+ const filePath = await findD365File(symbolIndex, objectType, objectName, modelName, workspacePath, explicitFilePath, args.packagePath);
876
+ if (!filePath) {
877
+ throw new Error(`File not found for ${objectType} "${objectName}".\n\n` +
878
+ `Retry options (do NOT use PowerShell — this tool can handle it):\n` +
879
+ ` 1. Pass modelName="<YourModel>" — triggers filesystem lookup by path.\n` +
880
+ ` 2. Pass packagePath="<root that contains the model>" if the metadata lives outside the default PackagesLocalDirectory (e.g. a repo checkout).\n` +
881
+ ` 3. Pass filePath="K:\\\\AosService\\\\PackagesLocalDirectory\\\\<pkg>\\\\<model>\\\\${objectName}.xml" — bypasses all lookup.\n` +
882
+ ` 4. If the object was just created, re-run d365fo_file(action="create") first and use the returned path as filePath.\n\n` +
883
+ `If the file exists but bridge operations still fail to resolve the object, the C# bridge's metadata roots ` +
884
+ `(fixed at startup) likely don't include this model — set context.customPackagesPath / D365FO_CUSTOM_PACKAGES_PATH and restart the server.`);
885
+ }
886
+ // 1a. Path containment guard — every write target must live under a configured
887
+ // <PackagesLocalDirectory>/<Package>/<Model>/Ax<Type>/<File>.xml layout.
888
+ // Refuses path traversal via explicit filePath or JSON sourcePath (security-critical).
889
+ // A caller-supplied packagePath (metadata outside PLD, e.g. a repo checkout) is
890
+ // honored as an allowed root — findD365File already resolves against it, so the
891
+ // containment list must include it too or valid writes get wrongly rejected.
892
+ const extraRoots = args.packagePath ? [args.packagePath] : undefined;
893
+ const containment = await assertWritePathAllowed(filePath, modelName, { extraRoots });
894
+ if (!containment.ok) {
895
+ throw new Error(containment.reason || 'Path containment check failed');
896
+ }
897
+ // 1b. Model-ownership guard: refuse to modify objects in standard Microsoft models.
898
+ // This prevents accidental writes to ApplicationSuite, ApplicationFoundation, etc.
899
+ const resolvedModelFromPath = extractModelFromFilePath(filePath);
900
+ if (resolvedModelFromPath && isStandardModel(resolvedModelFromPath)) {
901
+ const configManager = getConfigManager();
902
+ const configuredModel = modelName || configManager.getModelName();
903
+ // Only block if the resolved model differs from the user's explicitly configured model.
904
+ // If user explicitly set modelName=ApplicationSuite, they know what they're doing.
905
+ if (!modelName || modelName !== resolvedModelFromPath) {
906
+ throw new Error(`⛔ Refusing to modify "${objectName}" — the resolved file belongs to standard Microsoft model "${resolvedModelFromPath}".\n\n` +
907
+ `Your configured model is "${configuredModel || '(not set)'}".\n` +
908
+ `Modifying standard objects is not permitted — it can corrupt the base application.\n\n` +
909
+ `To extend a standard object, create an extension instead:\n` +
910
+ ` • Table: d365fo_file(action="create", objectType="table-extension", objectName="${objectName}.${configuredModel || 'YourModel'}Extension")\n` +
911
+ ` • Class: d365fo_file(action="create", objectType="class-extension", objectName="${objectName}_Extension")\n` +
912
+ ` • Form: d365fo_file(action="create", objectType="form-extension", objectName="${objectName}.${configuredModel || 'YourModel'}Extension")`);
913
+ }
914
+ }
915
+ // 2. Resolve actual XML file path (DB may store JSON metadata with sourcePath)
916
+ let actualFilePath = filePath;
917
+ try {
918
+ const fileContent = await fs.readFile(filePath, 'utf-8');
919
+ const trimmed = fileContent.trimStart();
920
+ if (trimmed.startsWith('{')) {
921
+ const data = JSON.parse(fileContent);
922
+ if (data.sourcePath) {
923
+ // Re-validate the indirect path: sourcePath also comes from user-influenced data.
924
+ const srcContainment = await assertWritePathAllowed(data.sourcePath, modelName, { extraRoots });
925
+ if (!srcContainment.ok) {
926
+ throw new Error(`sourcePath rejected: ${srcContainment.reason}`);
927
+ }
928
+ actualFilePath = srcContainment.canonicalPath || data.sourcePath;
929
+ }
930
+ else {
931
+ throw new Error(`Metadata file has no sourcePath: ${filePath}`);
932
+ }
933
+ }
934
+ }
935
+ catch (readError) {
936
+ if (readError instanceof SyntaxError || (readError instanceof Error && readError.message.includes('sourcePath'))) {
937
+ throw readError;
938
+ }
939
+ const isRelative = !path.isAbsolute(filePath);
940
+ const hint = isRelative
941
+ ? ' The path is relative — the symbol DB returned a build-agent path. ' +
942
+ 'Pass filePath="<absolute path>" or modelName="<YourModel>" so the tool can locate the file on disk.'
943
+ : '';
944
+ throw new Error(`Cannot read file: ${filePath}${hint}`);
945
+ }
946
+ // 3. Create backup of the actual XML file. When the target is NOT inside a
947
+ // git work tree, the documented undo path (undo_last_modification →
948
+ // git checkout) cannot revert the change — force a backup even with
949
+ // createBackup=false so a bad modify is never unrecoverable.
950
+ const backupNote = await ensureRecoverableModification(actualFilePath, createBackup);
951
+ // 3b. Derive the authoritative object name from the resolved file path.
952
+ // The caller may pass objectName="RentEquipment" while the file on disk
953
+ // is ContosoRentEquipment.xml (auto-prefixed at create time). The C# bridge
954
+ // resolves objects by name from its metadata model — if the name doesn't
955
+ // match the file it will always return null, regardless of refreshes.
956
+ // Use path.win32.basename so Windows backslash paths are handled correctly
957
+ // on all platforms (path.posix.basename treats '\\' as a regular character).
958
+ const fileBaseName = path.win32.basename(actualFilePath, '.xml');
959
+ const bridgeObjectName = fileBaseName !== objectName ? fileBaseName : objectName;
960
+ if (bridgeObjectName !== objectName) {
961
+ console.error(`[modify_d365fo_file] ℹ️ objectName "${objectName}" → resolved to "${bridgeObjectName}" from file basename`);
962
+ objectName = bridgeObjectName;
963
+ }
964
+ // ── Bridge-only modify via IMetadataProvider.Update() ────────────────────
965
+ // ALL modify operations go through the C# bridge. The bridge reads, modifies,
966
+ // and writes via the official D365FO metadata API — no xml2js needed.
967
+ // If the bridge is unavailable or fails, we throw an error (no fallback).
968
+ if (!context?.bridge) {
969
+ throw new Error('C# metadata bridge is not available. The bridge is required for all modify operations.\n' +
970
+ 'Start the bridge by building the D365MetadataBridge project and restarting the MCP server.');
971
+ }
972
+ if (!canBridgeModify(objectType, operation)) {
973
+ throw new Error(`Operation '${operation}' on object type '${objectType}' is not supported by the bridge.`);
974
+ }
975
+ let bridgeResult = null;
976
+ let _bridgeRetried = false;
977
+ // Retry loop: on the first null result with all required params present,
978
+ // refresh the bridge provider (picks up objects created this session) and
979
+ // re-run the operation once. Max 1 auto-refresh retry (_bridgeRetried guard).
980
+ _bridgeRetry: do {
981
+ bridgeResult = null;
982
+ switch (operation) {
983
+ case 'add-method': {
984
+ // sourceCode and methodCode are aliases; sourceCode wins when both are set.
985
+ const methodSource = args.sourceCode ?? args.methodCode;
986
+ if (methodSource) {
987
+ // A single call may carry several methods — split and add each as its own
988
+ // <Method> so callers don't have to issue one tool call per method.
989
+ const bodies = countTopLevelMethodBodies(methodSource) > 1
990
+ ? splitTopLevelMethodBodies(methodSource)
991
+ : [methodSource];
992
+ if (bodies.length > 1) {
993
+ const added = [];
994
+ let lastResult = null;
995
+ for (const body of bodies) {
996
+ const mName = extractMethodNameFromSource(body);
997
+ if (!mName) {
998
+ // A body without a derivable method name is typically a class/extension declaration
999
+ // (e.g. "[ExtensionOf(tableStr(T))] final class T_Extension { }"). Such blocks are
1000
+ // NOT methods and do not belong in add-method — skip and continue.
1001
+ // The regex handles: optional attribute block(s) → optional modifiers (final/abstract/
1002
+ // public/static/…) → class or interface keyword.
1003
+ const trimmed = body.trim();
1004
+ if (/^\s*(?:\[[^\]]*\]\s*)*(?:(?:public|private|protected|final|abstract|static|internal|sealed)\s+)*(?:class|interface)\b/i.test(trimmed)) {
1005
+ console.warn(`[add-method] Skipping class/interface declaration block (not a method): ${trimmed.slice(0, 80)}...`);
1006
+ continue;
1007
+ }
1008
+ throw new Error(`⛔ add-method: could not derive a method name from one of the ${bodies.length} method bodies. ` +
1009
+ `Ensure each method has a complete signature (e.g. "public void foo()").`);
1010
+ }
1011
+ lastResult = await bridgeAddMethod(context.bridge, objectType, objectName, mName, body);
1012
+ if (!lastResult) {
1013
+ throw new Error(`Bridge add-method failed for '${mName}' (${added.length} of ${bodies.length} method(s) added successfully: ${added.join(', ') || 'none'}).`);
1014
+ }
1015
+ added.push(mName);
1016
+ }
1017
+ // Summarize as a single result for the downstream success message.
1018
+ bridgeResult = lastResult
1019
+ ? { ...lastResult, message: `Added ${added.length} methods: ${added.join(', ')}` }
1020
+ : null;
1021
+ }
1022
+ else if (args.methodName) {
1023
+ bridgeResult = await bridgeAddMethod(context.bridge, objectType, objectName, args.methodName, methodSource);
1024
+ }
1025
+ }
1026
+ break;
1027
+ }
1028
+ case 'add-display-method': {
1029
+ // Explicit source wins; otherwise generate a stub from displayMethodReturnEdt.
1030
+ let methodSource = args.sourceCode ?? args.methodCode;
1031
+ const methodName = args.methodName;
1032
+ if (!methodSource && methodName && args.displayMethodReturnEdt) {
1033
+ methodSource = generateDisplayMethodSource(methodName, args.displayMethodReturnEdt);
1034
+ generationNote =
1035
+ `\n\n> 🔧 Display method \`${methodName}\` generated returning ` +
1036
+ `\`${args.displayMethodReturnEdt}\` (stub — fill in the computation).`;
1037
+ }
1038
+ if (methodName && methodSource) {
1039
+ bridgeResult = await bridgeAddMethod(context.bridge, objectType, objectName, methodName, methodSource);
1040
+ }
1041
+ break;
1042
+ }
1043
+ case 'add-table-method': {
1044
+ // Explicit source wins; otherwise generate from tableMethodType.
1045
+ let methodSource = args.sourceCode ?? args.methodCode;
1046
+ let methodName = args.methodName;
1047
+ if (!methodSource && args.tableMethodType) {
1048
+ const gen = generateTableMethodSource(objectName, args.tableMethodType, args.tableKeyField, symbolIndex.getReadDb());
1049
+ methodName = gen.methodName;
1050
+ methodSource = gen.source;
1051
+ generationNote =
1052
+ `\n\n> 🔧 Table method \`${gen.methodName}\` generated from ` +
1053
+ `tableMethodType="${args.tableMethodType}".` +
1054
+ (gen.note ? `\n> ${gen.note}` : '');
1055
+ }
1056
+ // When caller supplies custom source (methodCode/sourceCode) + tableMethodType
1057
+ // but omits methodName, the method name equals the tableMethodType value
1058
+ // (find / exist / findByRecId / validateWrite / validateDelete / initValue).
1059
+ if (!methodName && args.tableMethodType && methodSource) {
1060
+ methodName = args.tableMethodType;
1061
+ }
1062
+ if (methodName && methodSource) {
1063
+ bridgeResult = await bridgeAddMethod(context.bridge, objectType, objectName, methodName, methodSource);
1064
+ }
1065
+ break;
1066
+ }
1067
+ case 'remove-method': {
1068
+ if (args.methodName) {
1069
+ bridgeResult = await bridgeRemoveMethod(context.bridge, objectType, objectName, args.methodName);
1070
+ }
1071
+ break;
1072
+ }
1073
+ case 'add-field': {
1074
+ if (args.fieldName && args.fieldType) {
1075
+ // fieldType is the EDT name; fieldBaseType is the primitive base type.
1076
+ // When fieldBaseType is omitted, auto-resolve it from the symbol index so the
1077
+ // correct AxTableField XML element is emitted. Always pass fieldType as edtName
1078
+ // so the <ExtendedDataType> reference is always written — previously, omitting
1079
+ // fieldBaseType left edtName undefined and produced fields with no EDT reference.
1080
+ const edtName = args.fieldType;
1081
+ let baseType = args.fieldBaseType ?? '';
1082
+ if (!baseType) {
1083
+ try {
1084
+ const rdb = symbolIndex.getReadDb();
1085
+ baseType = resolveEdtBaseTypeForField(edtName, rdb);
1086
+ }
1087
+ catch {
1088
+ baseType = edtName; // bridge will apply its own name heuristics
1089
+ }
1090
+ }
1091
+ bridgeResult = await bridgeAddField(context.bridge, objectName, args.fieldName, baseType, edtName, args.fieldMandatory, args.fieldLabel);
1092
+ }
1093
+ break;
1094
+ }
1095
+ case 'modify-field': {
1096
+ if (args.fieldName) {
1097
+ // Map the field-* params onto the bare prop keys the bridge expects.
1098
+ const fieldProps = {};
1099
+ if (args.fieldLabel)
1100
+ fieldProps.label = args.fieldLabel;
1101
+ if (args.fieldHelpText)
1102
+ fieldProps.helpText = args.fieldHelpText;
1103
+ if (args.fieldMandatory !== undefined)
1104
+ fieldProps.mandatory = String(args.fieldMandatory);
1105
+ if (args.fieldType)
1106
+ fieldProps.edt = args.fieldType;
1107
+ if (args.fieldEnumType)
1108
+ fieldProps.enumType = args.fieldEnumType;
1109
+ if (args.fieldStringSize)
1110
+ fieldProps.stringSize = String(args.fieldStringSize);
1111
+ bridgeResult = await bridgeModifyField(context.bridge, objectName, args.fieldName, Object.keys(fieldProps).length > 0 ? fieldProps : undefined);
1112
+ }
1113
+ break;
1114
+ }
1115
+ case 'rename-field': {
1116
+ if (args.fieldName && args.fieldNewName) {
1117
+ bridgeResult = await bridgeRenameField(context.bridge, objectName, args.fieldName, args.fieldNewName);
1118
+ }
1119
+ break;
1120
+ }
1121
+ case 'remove-field': {
1122
+ if (args.fieldName) {
1123
+ bridgeResult = await bridgeRemoveField(context.bridge, objectName, args.fieldName);
1124
+ }
1125
+ break;
1126
+ }
1127
+ case 'replace-all-fields': {
1128
+ if (args.fields) {
1129
+ const rawFields = args.fields;
1130
+ const resolvedFields = rawFields.map((f) => {
1131
+ if (!f.type && f.edt) {
1132
+ try {
1133
+ const rdb = symbolIndex.getReadDb();
1134
+ return { ...f, type: resolveEdtBaseTypeForField(f.edt, rdb) };
1135
+ }
1136
+ catch {
1137
+ return f;
1138
+ }
1139
+ }
1140
+ return f;
1141
+ });
1142
+ bridgeResult = await bridgeReplaceAllFields(context.bridge, objectName, resolvedFields);
1143
+ }
1144
+ break;
1145
+ }
1146
+ case 'add-index': {
1147
+ if (args.indexName) {
1148
+ // indexFields is documented (Zod + MCP schema) as [{ fieldName, direction? }],
1149
+ // but the bridge's addIndex expects a flat string[] of field names. Map the
1150
+ // objects to their fieldName here — passing the objects straight through makes
1151
+ // the C# side deserialize [{fieldName:…}] into List<string>, which throws and
1152
+ // surfaces as a null bridge result (misreported as "could not resolve table").
1153
+ const indexFieldNames = Array.isArray(args.indexFields)
1154
+ ? args.indexFields.map((f) => (typeof f === 'string' ? f : f?.fieldName)).filter(Boolean)
1155
+ : undefined;
1156
+ bridgeResult = await bridgeAddIndex(context.bridge, objectName, args.indexName, indexFieldNames, args.indexAllowDuplicates, args.indexAlternateKey);
1157
+ }
1158
+ break;
1159
+ }
1160
+ case 'remove-index': {
1161
+ if (args.indexName) {
1162
+ bridgeResult = await bridgeRemoveIndex(context.bridge, objectName, args.indexName);
1163
+ }
1164
+ break;
1165
+ }
1166
+ case 'add-relation': {
1167
+ if (args.relationName && args.relatedTable) {
1168
+ // relationConstraints is documented as [{ fieldName, relatedFieldName }], but the
1169
+ // bridge/C# WriteRelationConstraint deserializes the JSON keys { field, relatedField }.
1170
+ // Without this remap the keys don't match: C# sees no `field`/`relatedField`, leaves
1171
+ // both null, and silently writes a relation with EMPTY constraints (no hard error —
1172
+ // the corruption only surfaces at compile time). Map the field names explicitly.
1173
+ const constraints = Array.isArray(args.relationConstraints)
1174
+ ? args.relationConstraints.map((c) => ({
1175
+ field: c?.field ?? c?.fieldName,
1176
+ relatedField: c?.relatedField ?? c?.relatedFieldName,
1177
+ }))
1178
+ : undefined;
1179
+ bridgeResult = await bridgeAddRelation(context.bridge, objectName, args.relationName, args.relatedTable, constraints);
1180
+ }
1181
+ break;
1182
+ }
1183
+ case 'remove-relation': {
1184
+ if (args.relationName) {
1185
+ bridgeResult = await bridgeRemoveRelation(context.bridge, objectName, args.relationName);
1186
+ }
1187
+ break;
1188
+ }
1189
+ case 'add-field-group': {
1190
+ if (args.fieldGroupName) {
1191
+ bridgeResult = await bridgeAddFieldGroup(context.bridge, objectName, args.fieldGroupName, args.fieldGroupLabel, args.fieldGroupFields);
1192
+ }
1193
+ break;
1194
+ }
1195
+ case 'remove-field-group': {
1196
+ if (args.fieldGroupName) {
1197
+ bridgeResult = await bridgeRemoveFieldGroup(context.bridge, objectName, args.fieldGroupName);
1198
+ }
1199
+ break;
1200
+ }
1201
+ case 'add-field-to-field-group': {
1202
+ if (args.fieldGroupName && args.fieldName) {
1203
+ bridgeResult = await bridgeAddFieldToFieldGroup(context.bridge, objectName, args.fieldGroupName, args.fieldName);
1204
+ }
1205
+ break;
1206
+ }
1207
+ case 'modify-property': {
1208
+ if (args.propertyPath && args.propertyValue !== undefined) {
1209
+ // ── Semantic guard for AxEdtExtension property changes ───────────────
1210
+ // D365FO silently accepts illegal extension edits (e.g. widening
1211
+ // StringSize on a derived EDT) but the change is ineffective at
1212
+ // runtime and corrupts the metadata model. Block them here with a
1213
+ // clear explanation of the proper alternative.
1214
+ if (objectType === 'edt-extension') {
1215
+ const guard = await validateEdtExtensionChange(objectName, args.propertyPath, String(args.propertyValue), symbolIndex.getReadDb(), context.bridge);
1216
+ if (!guard.ok) {
1217
+ return {
1218
+ content: [{ type: 'text', text: guard.message ?? 'EDT extension change rejected.' }],
1219
+ isError: true,
1220
+ };
1221
+ }
1222
+ }
1223
+ bridgeResult = await bridgeSetProperty(context.bridge, objectType, objectName, args.propertyPath, args.propertyValue);
1224
+ // Fallback: some object types (confirmed: AxForm) are rejected by the
1225
+ // bridge outright for modify-property even though the property is a
1226
+ // plain text element trivially editable by string replacement.
1227
+ if (!bridgeResult || !bridgeResult.success) {
1228
+ const xmlFallbackResult = await directXmlModifyProperty(actualFilePath, args.propertyPath, String(args.propertyValue));
1229
+ if (xmlFallbackResult) {
1230
+ bridgeResult = xmlFallbackResult;
1231
+ }
1232
+ }
1233
+ }
1234
+ break;
1235
+ }
1236
+ case 'replace-code': {
1237
+ // Auto-detect common mistake: agent sends sourceCode/methodCode instead of oldCode/newCode
1238
+ const hasOldNew = args.oldCode && args.newCode !== undefined;
1239
+ const sentSourceCode = args.sourceCode || args.methodCode;
1240
+ if (!hasOldNew && sentSourceCode) {
1241
+ throw new Error(`⛔ replace-code requires 'oldCode' and 'newCode' — NOT 'sourceCode'/'methodCode'.\n\n` +
1242
+ `You sent sourceCode/methodCode but replace-code needs:\n` +
1243
+ ` • oldCode = the exact existing snippet to find\n` +
1244
+ ` • newCode = the replacement snippet\n\n` +
1245
+ `Example:\n` +
1246
+ ` d365fo_file(action="modify", objectType="form", objectName="MyForm",\n` +
1247
+ ` operation="replace-code",\n` +
1248
+ ` methodName="PostButton.clicked",\n` +
1249
+ ` oldCode="ttsbegin;",\n` +
1250
+ ` newCode="")\n\n` +
1251
+ `If you want to replace an entire existing method, pass the full old method source as oldCode and the full new method source as newCode so the edit stays in place. Use remove-method + add-method only when you intentionally want a remove/add operation.`);
1252
+ }
1253
+ if (hasOldNew) {
1254
+ // Try bridge first
1255
+ bridgeResult = await bridgeReplaceCode(context.bridge, objectType, objectName, args.methodName, args.oldCode, args.newCode);
1256
+ // Fallback: if bridge returns null (unsupported type or not connected)
1257
+ // or success=false (SDK couldn't find the code — e.g. form control override),
1258
+ // do direct string replacement in the XML file.
1259
+ // This handles form control override methods which the SDK may not expose.
1260
+ if (!bridgeResult || !bridgeResult.success) {
1261
+ const xmlFallbackResult = await directXmlReplaceCode(actualFilePath, args.oldCode, args.newCode);
1262
+ if (xmlFallbackResult) {
1263
+ bridgeResult = xmlFallbackResult;
1264
+ }
1265
+ }
1266
+ }
1267
+ else {
1268
+ throw new Error(`replace-code requires both 'oldCode' and 'newCode' parameters.\n` +
1269
+ ` oldCode: ${args.oldCode ? 'provided' : '⛔ MISSING'}\n` +
1270
+ ` newCode: ${args.newCode !== undefined ? 'provided' : '⛔ MISSING'}\n` +
1271
+ `Note: 'sourceCode' is NOT an alias for replace-code — you must use 'oldCode' and 'newCode'.\n\n` +
1272
+ renderOpSpec('replace-code'));
1273
+ }
1274
+ break;
1275
+ }
1276
+ case 'add-enum-value': {
1277
+ if (args.enumValueName !== undefined) {
1278
+ bridgeResult = await bridgeAddEnumValue(context.bridge, objectName, args.enumValueName, args.enumValueInt ?? 0, args.enumValueLabel, args.enumValueCountryRegionCodes);
1279
+ }
1280
+ break;
1281
+ }
1282
+ case 'modify-enum-value': {
1283
+ if (args.enumValueName) {
1284
+ const evProps = {};
1285
+ if (args.enumValueNewName)
1286
+ evProps.name = args.enumValueNewName;
1287
+ if (args.enumValueLabel)
1288
+ evProps.label = args.enumValueLabel;
1289
+ if (args.enumValueInt !== undefined)
1290
+ evProps.value = String(args.enumValueInt);
1291
+ bridgeResult = await bridgeModifyEnumValue(context.bridge, objectName, args.enumValueName, Object.keys(evProps).length > 0 ? evProps : undefined);
1292
+ }
1293
+ break;
1294
+ }
1295
+ case 'remove-enum-value': {
1296
+ if (args.enumValueName) {
1297
+ bridgeResult = await bridgeRemoveEnumValue(context.bridge, objectName, args.enumValueName);
1298
+ }
1299
+ break;
1300
+ }
1301
+ case 'add-control': {
1302
+ if (args.controlName && args.parentControl) {
1303
+ // The tool never exposes a `controlType` input (not in the Zod schema below),
1304
+ // so this always used to fall back to the literal string "String" — every
1305
+ // control, regardless of the bound field's actual type, was created as a plain
1306
+ // string edit control. Infer a better default from the field name heuristic
1307
+ // (the same one used for table field base-type resolution) when a
1308
+ // controlDataField is given; this is a best-effort name heuristic, not an
1309
+ // index/bridge EDT lookup (that would need resolving controlDataSource, a form
1310
+ // DATA SOURCE name, back to its bound table — out of scope here), but it is
1311
+ // strictly better than always guessing "String".
1312
+ const resolvedControlType = args.controlType
1313
+ ?? (args.controlDataField ? heuristicEdtBaseType(args.controlDataField) : undefined)
1314
+ ?? 'String';
1315
+ bridgeResult = await bridgeAddControl(context.bridge, objectName, args.controlName, args.parentControl, resolvedControlType, args.controlDataSource, args.controlDataField, args.controlLabel);
1316
+ // Fallback: the bridge's AddControl resolves its target via _provider.Forms,
1317
+ // which can never find a form EXTENSION (named "Base.Suffix") — it always
1318
+ // reports 'Form "<ext>" not found', regardless of metadata-root freshness.
1319
+ // For form extensions, write the control element straight into the XML.
1320
+ if (objectType === 'form-extension' && (!bridgeResult || !bridgeResult.success)) {
1321
+ const xmlFallbackResult = await directXmlAddControl(actualFilePath, args.controlName, args.parentControl, resolvedControlType, args.controlDataSource, args.controlDataField, args.controlLabel);
1322
+ if (xmlFallbackResult)
1323
+ bridgeResult = xmlFallbackResult;
1324
+ }
1325
+ }
1326
+ break;
1327
+ }
1328
+ case 'add-data-source': {
1329
+ if (args.dataSourceName && args.dataSourceTable) {
1330
+ bridgeResult = await bridgeAddDataSource(context.bridge, objectType, objectName, args.dataSourceName, args.dataSourceTable, args.joinSource, args.linkType);
1331
+ }
1332
+ break;
1333
+ }
1334
+ case 'add-field-modification': {
1335
+ if (args.fieldName) {
1336
+ bridgeResult = await bridgeAddFieldModification(context.bridge, objectName, args.fieldName, args.fieldLabel, args.fieldMandatory);
1337
+ }
1338
+ break;
1339
+ }
1340
+ case 'add-menu-item-to-menu': {
1341
+ if (args.menuItemToAdd) {
1342
+ bridgeResult = await bridgeAddMenuItemToMenu(context.bridge, objectName, args.menuItemToAdd, args.menuItemToAddType);
1343
+ // Fallback: bridge requires the menu to exist in its loaded metadata roots.
1344
+ // Newly created menus aren't there yet — write directly to the XML file.
1345
+ if (!bridgeResult || !bridgeResult.success) {
1346
+ const xmlFallbackResult = await directXmlAddMenuItemToMenu(actualFilePath, args.menuItemToAdd, args.menuItemToAddType ?? 'display');
1347
+ if (xmlFallbackResult) {
1348
+ bridgeResult = xmlFallbackResult;
1349
+ }
1350
+ }
1351
+ }
1352
+ break;
1353
+ }
1354
+ default:
1355
+ throw new Error(`Unsupported operation: ${operation}`);
1356
+ }
1357
+ if (!bridgeResult) {
1358
+ // Required params + full per-op specs live in the central registry
1359
+ // (d365foFileOpSpecs.ts) — the published schema only advertises a
1360
+ // free-form `params` object, so this error must carry the whole spec.
1361
+ const required = getRequiredParams(operation);
1362
+ // Treat a supplied alias as satisfying the requirement.
1363
+ const isProvided = (p) => args[p] !== undefined || (OP_PARAM_ALIASES[p] ?? []).some(a => args[a] !== undefined);
1364
+ const missing = required.filter(p => !isProvided(p));
1365
+ // Auto-refresh retry: all required params are present but bridge returned
1366
+ // null — the object was likely written this session and isn't in the
1367
+ // bridge's metadata model yet (roots are fixed at startup). Refresh once
1368
+ // and re-run the operation. Afterwards the normal error path takes over.
1369
+ if (missing.length === 0 && !_bridgeRetried && context.bridge) {
1370
+ console.error(`[modify_d365fo_file] ⚠️ '${operation}' on '${objectName}' returned null — ` +
1371
+ `refreshing bridge provider and retrying once`);
1372
+ try {
1373
+ await bridgeRefreshProvider(context.bridge);
1374
+ }
1375
+ catch { /* best-effort */ }
1376
+ _bridgeRetried = true;
1377
+ continue _bridgeRetry;
1378
+ }
1379
+ // Two distinct causes produce a null bridge result; pick the message that
1380
+ // matches reality instead of always blaming missing parameters.
1381
+ if (missing.length > 0) {
1382
+ const missingList = missing.map(p => ` ⛔ ${p}: MISSING`);
1383
+ const providedList = required.filter(isProvided).map(p => ` ✅ ${p}: provided`);
1384
+ throw new Error(`Bridge operation '${operation}' returned null — required parameters are missing.\n` +
1385
+ `Required parameters for '${operation}':\n${[...providedList, ...missingList].join('\n')}\n` +
1386
+ `Provided args: ${Object.keys(args).filter(k => args[k] !== undefined).join(', ')}\n\n` +
1387
+ renderOpSpec(operation));
1388
+ }
1389
+ // All required params were supplied, yet the bridge returned null (it never
1390
+ // attempted the op — e.g. provider not ready). Surface the actionable
1391
+ // same-session resolution guidance.
1392
+ throw new Error(unresolvedObjectError(operation, objectType, objectName, actualFilePath));
1393
+ }
1394
+ // Bridge executed but reported failure. A resolution failure (object created
1395
+ // this session, not yet in the provider's fixed-root model) gets the same
1396
+ // one-shot refresh+retry the null path gets; any other failure falls through
1397
+ // to the real-message throw below — no longer masked as a generic guess.
1398
+ if (!bridgeResult.success &&
1399
+ !_bridgeRetried &&
1400
+ context.bridge &&
1401
+ isUnresolvedObjectError(bridgeResult.message)) {
1402
+ console.error(`[modify_d365fo_file] ⚠️ '${operation}' on '${objectName}' failed (${bridgeResult.message}) — ` +
1403
+ `refreshing bridge provider and retrying once`);
1404
+ try {
1405
+ await bridgeRefreshProvider(context.bridge);
1406
+ }
1407
+ catch { /* best-effort */ }
1408
+ _bridgeRetried = true;
1409
+ continue _bridgeRetry;
1410
+ }
1411
+ break _bridgeRetry;
1412
+ } while (true); // end of retry loop
1413
+ if (!bridgeResult.success) {
1414
+ // Surface the real bridge error. For an object-resolution failure keep the
1415
+ // actionable same-session fallback guidance, now including exactly what the
1416
+ // bridge reported instead of a generic "could not resolve" guess.
1417
+ if (isUnresolvedObjectError(bridgeResult.message)) {
1418
+ throw new Error(unresolvedObjectError(operation, objectType, objectName, actualFilePath, bridgeResult.message));
1419
+ }
1420
+ let opErrorMsg = `Bridge operation '${operation}' failed: ${bridgeResult.message}`;
1421
+ if (operation === 'replace-code' && /oldCode not found/i.test(bridgeResult.message ?? '')) {
1422
+ opErrorMsg +=
1423
+ `\n\n💡 Tip: The oldCode must match the exact source currently on disk.\n` +
1424
+ ` • Fetch the current source first: get_object_info(objectType="${objectType}", name="${objectName}")\n` +
1425
+ ` • Then copy the exact snippet from the output as oldCode.\n` +
1426
+ ` • Alternative: use add-method with the complete new method body — it overwrites the existing method.`;
1427
+ }
1428
+ throw new Error(opErrorMsg);
1429
+ }
1430
+ console.error(`[modify_d365fo_file] ✅ Bridge ${operation}: ${bridgeResult.message}`);
1431
+ // Post-write validation (best-effort, fire-and-forget).
1432
+ // Not awaited: the validation goes through the sequential bridge stdin/stdout
1433
+ // pipe and can take 60s+, which would block all subsequent MCP calls.
1434
+ // See: https://github.com/dynamics365ninja/d365fo-mcp-server/issues/407
1435
+ let bridgeValidation = '';
1436
+ bridgeValidateAfterWrite(context.bridge, objectType, objectName).then(validationMsg => {
1437
+ if (validationMsg) {
1438
+ console.error(`[modify_d365fo_file] Bridge validation: ${validationMsg}`);
1439
+ }
1440
+ }).catch(e => {
1441
+ console.error(`[modify_d365fo_file] Bridge validation skipped: ${e}`);
1442
+ });
1443
+ // Optionally add the file to the Visual Studio project
1444
+ let projectMessage = '';
1445
+ if (args.addToProject) {
1446
+ const configManager = getConfigManager();
1447
+ await configManager.ensureLoaded();
1448
+ let resolvedProjectPath = args.projectPath || await configManager.getProjectPath() || undefined;
1449
+ const resolvedSolutionPath = args.solutionPath || await configManager.getSolutionPath() || undefined;
1450
+ if (!resolvedProjectPath && resolvedSolutionPath) {
1451
+ resolvedProjectPath = await ProjectFileFinder.findProjectInSolution(resolvedSolutionPath, modelName || configManager.getModelName() || '') || undefined;
1452
+ }
1453
+ if (resolvedProjectPath) {
1454
+ try {
1455
+ await fs.access(resolvedProjectPath);
1456
+ const projectManager = new ProjectFileManager();
1457
+ const wasAdded = await projectManager.addToProject(resolvedProjectPath, objectType, objectName, actualFilePath);
1458
+ projectMessage = wasAdded
1459
+ ? `\n✅ Added to VS project: \`${resolvedProjectPath}\``
1460
+ : `\n📋 Already in VS project: \`${resolvedProjectPath}\``;
1461
+ }
1462
+ catch (projErr) {
1463
+ const errMsg = projErr instanceof Error ? projErr.message : String(projErr);
1464
+ projectMessage = `\n⚠️ File modified but could not add to VS project: ${errMsg}`;
1465
+ }
1466
+ }
1467
+ else {
1468
+ projectMessage =
1469
+ `\n⚠️ addToProject=true but no projectPath could be resolved.\n` +
1470
+ `Add \`projectPath\` to .mcp.json or pass it explicitly.`;
1471
+ }
1472
+ }
1473
+ // Advisory X++ select-statement lint on the source just written (add-method /
1474
+ // replace-code etc.). Non-blocking: surfaces a likely "WHERE after join" mistake
1475
+ // up front instead of letting it become a build error the agent hunts by hand.
1476
+ const xppLintWarnings = lintXppSelect(args.sourceCode ?? args.methodCode ?? args.newCode);
1477
+ const xppLintNote = xppLintWarnings.length > 0 ? `\n\n${xppLintWarnings.join('\n\n')}` : '';
1478
+ return {
1479
+ content: [
1480
+ {
1481
+ type: 'text',
1482
+ text: `✅ ${operation} on ${objectType} "${objectName}" — applied via IMetadataProvider.Update()\n\n` +
1483
+ `**File:** ${actualFilePath}${addControlNote}${generationNote}${bridgeValidation}${projectMessage}\n` +
1484
+ `🔧 API: ${bridgeResult.message}${xppLintNote}${backupNote}\n\n` +
1485
+ `**Next steps:**\n- Review changes in Visual Studio\n- Build the model to validate`,
1486
+ },
1487
+ ],
1488
+ };
1489
+ }
1490
+ catch (error) {
1491
+ return {
1492
+ content: [
1493
+ {
1494
+ type: 'text',
1495
+ text: `❌ Error modifying D365FO file: ${error instanceof Error ? error.message : 'Unknown error'}`,
1496
+ },
1497
+ ],
1498
+ isError: true,
1499
+ };
1500
+ }
1501
+ }
1502
+ /**
1503
+ * Find D365FO file path
1504
+ */
1505
+ async function findD365File(symbolIndex, objectType, objectName, modelName, _workspacePath, explicitFilePath, packagePath) {
1506
+ // Explicit path bypasses all lookup — use when caller knows the exact location
1507
+ // (e.g. the path was returned by create_d365fo_file).
1508
+ if (explicitFilePath) {
1509
+ return explicitFilePath;
1510
+ }
1511
+ // Resolve by the given name first; if that misses, retry once with the model
1512
+ // prefix applied. create_d365fo_file auto-prefixes new object names (e.g.
1513
+ // "RentEquipmentTable" → "ContosoRentEquipmentTable"), so a modify call with the bare
1514
+ // name would otherwise fail to locate the file. The bridge object name is then
1515
+ // re-derived from the resolved file's basename, so the prefixed name flows through.
1516
+ const direct = await resolveD365FileByName(symbolIndex, objectType, objectName, modelName, packagePath);
1517
+ if (direct)
1518
+ return direct;
1519
+ const prefix = resolveObjectPrefix(modelName || getConfigManager().getModelName() || '');
1520
+ if (prefix && !objectType.endsWith('-extension')) {
1521
+ const prefixed = applyObjectPrefix(objectName, prefix);
1522
+ if (prefixed && prefixed.toLowerCase() !== objectName.toLowerCase()) {
1523
+ const viaPrefixed = await resolveD365FileByName(symbolIndex, objectType, prefixed, modelName, packagePath);
1524
+ if (viaPrefixed) {
1525
+ console.error(`[modifyD365File] '${objectName}' not found — resolved via prefixed name '${prefixed}'`);
1526
+ return viaPrefixed;
1527
+ }
1528
+ }
1529
+ }
1530
+ return null;
1531
+ }
1532
+ /** Resolve a D365FO file by an exact object name (symbol DB, then filesystem). */
1533
+ async function resolveD365FileByName(symbolIndex, objectType, objectName, modelName, packagePath) {
1534
+ // Symbol DB only indexes a subset of types — for the rest go straight to filesystem.
1535
+ const dbTypeMap = {
1536
+ class: 'class',
1537
+ table: 'table',
1538
+ form: 'form',
1539
+ enum: 'enum',
1540
+ query: 'query',
1541
+ view: 'view',
1542
+ };
1543
+ const symbolType = dbTypeMap[objectType];
1544
+ // Query database when a symbol type mapping exists
1545
+ if (symbolType) {
1546
+ let dbResult = null;
1547
+ const rdb = symbolIndex.getReadDb();
1548
+ if (modelName) {
1549
+ const stmt = rdb.prepare(`
1550
+ SELECT file_path
1551
+ FROM symbols
1552
+ WHERE type = ? AND name = ? AND model = ?
1553
+ LIMIT 1
1554
+ `);
1555
+ const row = stmt.get(symbolType, objectName, modelName);
1556
+ dbResult = row ? row.file_path : null;
1557
+ }
1558
+ else {
1559
+ // No modelName specified — prefer the user's configured model to avoid
1560
+ // accidentally resolving to a standard Microsoft model (issue #369).
1561
+ const configuredModel = getConfigManager().getModelName();
1562
+ if (configuredModel) {
1563
+ const stmtPref = rdb.prepare(`
1564
+ SELECT file_path
1565
+ FROM symbols
1566
+ WHERE type = ? AND name = ? AND model = ?
1567
+ LIMIT 1
1568
+ `);
1569
+ const prefRow = stmtPref.get(symbolType, objectName, configuredModel);
1570
+ dbResult = prefRow ? prefRow.file_path : null;
1571
+ }
1572
+ if (!dbResult) {
1573
+ // Fallback: any model (still guarded by the standard-model check after findD365File)
1574
+ const stmt = rdb.prepare(`
1575
+ SELECT file_path
1576
+ FROM symbols
1577
+ WHERE type = ? AND name = ?
1578
+ ORDER BY model
1579
+ LIMIT 1
1580
+ `);
1581
+ const row = stmt.get(symbolType, objectName);
1582
+ dbResult = row ? row.file_path : null;
1583
+ }
1584
+ }
1585
+ // Only trust the DB path when it is an absolute path that actually exists on disk.
1586
+ // The DB file_path column stores paths from the CI build agent (e.g. C:\home\vsts\work\...)
1587
+ // which are never accessible at runtime. Relative paths (e.g. "ContosoExt/ContosoExt/AxClass/Foo.xml")
1588
+ // also come from this source and cannot be used directly.
1589
+ // Fall through to findD365FileOnDisk which builds the correct absolute path from config.
1590
+ //
1591
+ // Use cross-platform absolute detection so that Windows-style drive paths (C:\...)
1592
+ // are recognised as absolute even when the server runs on Linux/macOS (path.isAbsolute
1593
+ // returns false for Windows paths on POSIX hosts, causing spurious fallback loops).
1594
+ const isAbsoluteXPlat = (p) => path.isAbsolute(p) || /^[a-zA-Z]:[\\/]/.test(p) || /^\\\\/.test(p);
1595
+ if (dbResult && isAbsoluteXPlat(dbResult)) {
1596
+ try {
1597
+ await import('fs').then(m => m.promises.access(dbResult));
1598
+ return dbResult;
1599
+ }
1600
+ catch {
1601
+ // Absolute path from DB but not accessible — fall through to filesystem lookup
1602
+ console.error(`[modifyD365File] DB path not accessible: ${dbResult} — falling back to filesystem lookup`);
1603
+ }
1604
+ }
1605
+ else if (dbResult) {
1606
+ console.error(`[modifyD365File] DB returned relative path: ${dbResult} — falling back to filesystem lookup`);
1607
+ }
1608
+ }
1609
+ // Filesystem fallback: handles newly created files not yet in the symbol index,
1610
+ // and all types not covered by the symbol DB (edt, report, extensions, security, menu …).
1611
+ return findD365FileOnDisk(objectType, objectName, modelName, packagePath);
1612
+ }
1613
+ /**
1614
+ * Filesystem fallback for findD365File.
1615
+ * Constructs the expected AOT file path from config/env and checks if it exists on disk.
1616
+ * This handles objects that were just created and are not yet indexed in the symbol database.
1617
+ */
1618
+ export async function findD365FileOnDisk(objectType, objectName, modelName, explicitPackagePath) {
1619
+ const folderMap = {
1620
+ class: 'AxClass',
1621
+ table: 'AxTable',
1622
+ form: 'AxForm',
1623
+ enum: 'AxEnum',
1624
+ query: 'AxQuery',
1625
+ view: 'AxView',
1626
+ edt: 'AxEdt',
1627
+ 'data-entity': 'AxDataEntityView',
1628
+ report: 'AxReport',
1629
+ 'table-extension': 'AxTableExtension',
1630
+ 'class-extension': 'AxClass',
1631
+ 'form-extension': 'AxFormExtension',
1632
+ 'enum-extension': 'AxEnumExtension',
1633
+ 'edt-extension': 'AxEdtExtension',
1634
+ 'data-entity-extension': 'AxDataEntityViewExtension',
1635
+ 'menu-item-display': 'AxMenuItemDisplay',
1636
+ 'menu-item-action': 'AxMenuItemAction',
1637
+ 'menu-item-output': 'AxMenuItemOutput',
1638
+ 'menu-item-display-extension': 'AxMenuItemDisplayExtension',
1639
+ 'menu-item-action-extension': 'AxMenuItemActionExtension',
1640
+ 'menu-item-output-extension': 'AxMenuItemOutputExtension',
1641
+ menu: 'AxMenu',
1642
+ 'menu-extension': 'AxMenuExtension',
1643
+ 'security-privilege': 'AxSecurityPrivilege',
1644
+ 'security-duty': 'AxSecurityDuty',
1645
+ 'security-role': 'AxSecurityRole',
1646
+ };
1647
+ const objectFolder = folderMap[objectType];
1648
+ if (!objectFolder)
1649
+ return null;
1650
+ const configManager = getConfigManager();
1651
+ // Ensure .mcp.json is loaded — lazy init so this works even when
1652
+ // server startup did not call initializeConfig() before this tool ran.
1653
+ await configManager.ensureLoaded();
1654
+ // Resolve model name (same priority order as generateSmartTable):
1655
+ // 1. Explicit arg (skip placeholders like "any")
1656
+ // 2. .mcp.json context (modelName field or last segment of workspacePath)
1657
+ // 3. Auto-detected model name (async, from .rnrproj scan)
1658
+ // 4. D365FO_MODEL_NAME env var
1659
+ const resolvedModel = (modelName && modelName !== 'any' ? modelName : null) ||
1660
+ configManager.getModelName() ||
1661
+ (await configManager.getAutoDetectedModelName()) ||
1662
+ process.env.D365FO_MODEL_NAME ||
1663
+ null;
1664
+ if (!resolvedModel) {
1665
+ console.error('[modifyD365File] Filesystem fallback: could not resolve model name. ' +
1666
+ 'Provide modelName parameter, configure .mcp.json with modelName/projectPath, or set D365FO_MODEL_NAME env var.');
1667
+ return null;
1668
+ }
1669
+ const configPackagePath = configManager.getPackagePath() || fallbackPackagePath();
1670
+ // Resolve the custom write root (D365FO_CUSTOM_PACKAGES_PATH).
1671
+ // Try this before the MS PLD so we find repo-tracked objects first.
1672
+ const customWritePath = await configManager.getCustomPackagesPath();
1673
+ // Candidate 0: caller-supplied packagePath. Highest priority — the caller knows
1674
+ // the model lives outside the default PackagesLocalDirectory (e.g. a repo checkout).
1675
+ // Try both the package==model layout and a PackageResolver scan rooted here so a
1676
+ // package!=model layout (e.g. package "ACAUTOCONT" / model "AC AUTOCONT") still resolves.
1677
+ if (explicitPackagePath) {
1678
+ const directPath = path.join(explicitPackagePath, resolvedModel, resolvedModel, objectFolder, `${objectName}.xml`);
1679
+ try {
1680
+ await fs.access(directPath);
1681
+ console.error(`[modifyD365File] Found via explicit packagePath: ${directPath}`);
1682
+ return directPath;
1683
+ }
1684
+ catch { /* try resolver scan below */ }
1685
+ try {
1686
+ const resolver = new PackageResolver([explicitPackagePath]);
1687
+ const resolved = await resolver.resolve(resolvedModel);
1688
+ if (resolved) {
1689
+ const resolvedPath = path.join(resolved.rootPath, resolved.packageName, resolvedModel, objectFolder, `${objectName}.xml`);
1690
+ await fs.access(resolvedPath);
1691
+ console.error(`[modifyD365File] Found via explicit packagePath (PackageResolver): ${resolvedPath}`);
1692
+ return resolvedPath;
1693
+ }
1694
+ }
1695
+ catch { /* not found under explicit packagePath; fall through */ }
1696
+ }
1697
+ // Candidate 1: custom write root, package == model (most common for custom models)
1698
+ if (customWritePath) {
1699
+ const customCandidatePath = path.join(customWritePath, resolvedModel, resolvedModel, objectFolder, `${objectName}.xml`);
1700
+ try {
1701
+ await fs.access(customCandidatePath);
1702
+ console.error(`[modifyD365File] Found via custom packages path: ${customCandidatePath}`);
1703
+ return customCandidatePath;
1704
+ }
1705
+ catch {
1706
+ // Not at custom path; continue
1707
+ }
1708
+ }
1709
+ // Candidate 2: MS PLD / configured packagePath, package == model (traditional fallback)
1710
+ const candidatePath = path.join(configPackagePath, resolvedModel, resolvedModel, objectFolder, `${objectName}.xml`);
1711
+ try {
1712
+ await fs.access(candidatePath);
1713
+ console.error(`[modifyD365File] Found via filesystem fallback: ${candidatePath}`);
1714
+ return candidatePath;
1715
+ }
1716
+ catch {
1717
+ // Not at the default package==model path; try PackageResolver (UDE or custom layout)
1718
+ }
1719
+ // Candidate 3: PackageResolver scan — handles package != model layouts in both UDE
1720
+ // and traditional setups with a custom root
1721
+ try {
1722
+ const envType = await configManager.getDevEnvironmentType();
1723
+ const msPath = envType === 'ude' ? await configManager.getMicrosoftPackagesPath() : null;
1724
+ const roots = [explicitPackagePath, customWritePath, msPath, configPackagePath].filter(Boolean);
1725
+ const resolver = new PackageResolver(roots);
1726
+ const resolved = await resolver.resolve(resolvedModel);
1727
+ if (resolved) {
1728
+ const resolvedPath = path.join(resolved.rootPath, resolved.packageName, resolvedModel, objectFolder, `${objectName}.xml`);
1729
+ try {
1730
+ await fs.access(resolvedPath);
1731
+ console.error(`[modifyD365File] Found via PackageResolver: ${resolvedPath}`);
1732
+ return resolvedPath;
1733
+ }
1734
+ catch {
1735
+ // Not found at resolved path either
1736
+ }
1737
+ }
1738
+ }
1739
+ catch {
1740
+ // PackageResolver failed — skip silently
1741
+ }
1742
+ return null;
1743
+ }
1744
+ /**
1745
+ * Create file backup and verify it was written successfully.
1746
+ * Throws if the source file is missing or the copy fails, so callers
1747
+ * always know whether a valid backup exists before overwriting.
1748
+ * Returns the backup file path.
1749
+ */
1750
+ async function createFileBackup(filePath) {
1751
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').substring(0, 19);
1752
+ const backupPath = `${filePath}.backup-${timestamp}`;
1753
+ try {
1754
+ await fs.copyFile(filePath, backupPath);
1755
+ // Confirm the backup has non-zero size before proceeding
1756
+ const stat = await fs.stat(backupPath);
1757
+ if (stat.size === 0) {
1758
+ throw new Error('Backup file was created but is empty');
1759
+ }
1760
+ }
1761
+ catch (error) {
1762
+ throw new Error(`Failed to create backup at "${backupPath}": ${error instanceof Error ? error.message : String(error)}`);
1763
+ }
1764
+ return backupPath;
1765
+ }
1766
+ const execFileAsync = util.promisify(execFile);
1767
+ // Directory → inside-git-work-tree result, cached for the process lifetime so
1768
+ // repeated modifies don't re-spawn git for the same metadata folder.
1769
+ const gitWorkTreeCache = new Map();
1770
+ /**
1771
+ * Cheap check whether a file lives inside a git work tree — i.e. whether
1772
+ * undo_last_modification (git checkout) could revert a change to it.
1773
+ * git not installed, timeout, or any other error → treated as "not a repo".
1774
+ */
1775
+ async function isInsideGitWorkTree(filePath) {
1776
+ const dir = path.dirname(filePath);
1777
+ const cached = gitWorkTreeCache.get(dir);
1778
+ if (cached !== undefined)
1779
+ return cached;
1780
+ let inside = false;
1781
+ try {
1782
+ const { stdout } = await execFileAsync('git', ['rev-parse', '--is-inside-work-tree'], {
1783
+ cwd: dir,
1784
+ timeout: 5_000,
1785
+ windowsHide: true,
1786
+ });
1787
+ inside = stdout.trim() === 'true';
1788
+ }
1789
+ catch {
1790
+ inside = false;
1791
+ }
1792
+ gitWorkTreeCache.set(dir, inside);
1793
+ return inside;
1794
+ }
1795
+ /**
1796
+ * Backup guard for modify operations. Honors an explicit createBackup=true;
1797
+ * with createBackup=false it force-enables the backup when the target is not
1798
+ * inside a git work tree, because the documented undo path
1799
+ * (undo_last_modification → git checkout) only works inside a repo.
1800
+ * Returns a note to append to the success response ('' when no forced backup
1801
+ * was needed). Exported for unit tests.
1802
+ */
1803
+ export async function ensureRecoverableModification(actualFilePath, createBackup) {
1804
+ if (createBackup) {
1805
+ await createFileBackup(actualFilePath);
1806
+ return '';
1807
+ }
1808
+ if (await isInsideGitWorkTree(actualFilePath)) {
1809
+ return '';
1810
+ }
1811
+ const backupPath = await createFileBackup(actualFilePath);
1812
+ return (`\n\nℹ️ Target is not under git — created backup ${backupPath} automatically ` +
1813
+ `(undo_last_modification would not work here).`);
1814
+ }
1815
+ /**
1816
+ * Recursively walk an AxFormControl node forest and collect every control.
1817
+ */
1818
+ function walkFormControls(nodes, out, parentName, pathParts) {
1819
+ for (const node of nodes) {
1820
+ if (!node || typeof node !== 'object')
1821
+ continue;
1822
+ const name = Array.isArray(node.Name) ? node.Name[0] : (node.Name ?? '');
1823
+ if (!name)
1824
+ continue;
1825
+ const currentPath = [...pathParts, name];
1826
+ out.push({ name, parentName, pathStr: currentPath.join(' › ') });
1827
+ const cn = Array.isArray(node.Controls) ? node.Controls[0] : node.Controls;
1828
+ if (cn?.AxFormControl) {
1829
+ const children = Array.isArray(cn.AxFormControl) ? cn.AxFormControl : [cn.AxFormControl];
1830
+ walkFormControls(children, out, name, currentPath);
1831
+ }
1832
+ }
1833
+ }
1834
+ /**
1835
+ * Extract all controls from a parsed AxForm xmlObj.
1836
+ */
1837
+ function allControlsFromFormXmlObj(xmlObj) {
1838
+ const results = [];
1839
+ const axForm = xmlObj.AxForm;
1840
+ if (!axForm)
1841
+ return results;
1842
+ const designNode = Array.isArray(axForm.Design) ? axForm.Design[0] : axForm.Design;
1843
+ if (!designNode)
1844
+ return results;
1845
+ let rootNodes = [];
1846
+ // AxFormDesign wrapper (standard D365FO 10.0 format)
1847
+ if (designNode.AxFormDesign) {
1848
+ const fds = Array.isArray(designNode.AxFormDesign) ? designNode.AxFormDesign : [designNode.AxFormDesign];
1849
+ for (const fd of fds) {
1850
+ const cn = Array.isArray(fd.Controls) ? fd.Controls[0] : fd.Controls;
1851
+ if (cn?.AxFormControl) {
1852
+ const items = Array.isArray(cn.AxFormControl) ? cn.AxFormControl : [cn.AxFormControl];
1853
+ rootNodes = rootNodes.concat(items);
1854
+ }
1855
+ }
1856
+ }
1857
+ else if (designNode.Controls) {
1858
+ const cn = Array.isArray(designNode.Controls) ? designNode.Controls[0] : designNode.Controls;
1859
+ if (cn?.AxFormControl) {
1860
+ rootNodes = Array.isArray(cn.AxFormControl) ? cn.AxFormControl : [cn.AxFormControl];
1861
+ }
1862
+ }
1863
+ walkFormControls(rootNodes, results, null, []);
1864
+ return results;
1865
+ }
1866
+ /**
1867
+ * Locate the base form XML on disk, trying DB path → remapped path → filesystem scan.
1868
+ * Returns raw XML content, or null if not accessible.
1869
+ */
1870
+ export async function findBaseFormXml(baseFormName, symbolIndex) {
1871
+ // Helper: read a file, transparently following JSON metadata proxies.
1872
+ async function tryRead(p) {
1873
+ try {
1874
+ const raw = await fs.readFile(p, 'utf-8');
1875
+ if (raw.trimStart().startsWith('{')) {
1876
+ const data = JSON.parse(raw);
1877
+ if (data.sourcePath) {
1878
+ try {
1879
+ return await fs.readFile(data.sourcePath, 'utf-8');
1880
+ }
1881
+ catch {
1882
+ return null;
1883
+ }
1884
+ }
1885
+ return null;
1886
+ }
1887
+ return raw;
1888
+ }
1889
+ catch {
1890
+ return null;
1891
+ }
1892
+ }
1893
+ // 1. Symbol DB lookup
1894
+ let dbFilePath = null;
1895
+ try {
1896
+ const rdb = symbolIndex.getReadDb();
1897
+ const row = rdb.prepare(`SELECT file_path FROM symbols WHERE type = 'form' AND name = ? LIMIT 1`).get(baseFormName);
1898
+ if (row?.file_path)
1899
+ dbFilePath = row.file_path;
1900
+ }
1901
+ catch { /* ignore */ }
1902
+ if (dbFilePath) {
1903
+ // Try absolute DB path as-is
1904
+ const direct = await tryRead(dbFilePath);
1905
+ if (direct)
1906
+ return direct;
1907
+ // DB stored a relative path — join with configured packagePath
1908
+ if (!path.isAbsolute(dbFilePath)) {
1909
+ const cm = getConfigManager();
1910
+ await cm.ensureLoaded();
1911
+ const pkgPath = cm.getPackagePath() || fallbackPackagePath();
1912
+ const abs = await tryRead(path.join(pkgPath, dbFilePath));
1913
+ if (abs)
1914
+ return abs;
1915
+ }
1916
+ // Build-agent path remapping (e.g. /home/vsts/... → local PackagesLocalDirectory)
1917
+ const remapped = await resolveDbPathLocally(dbFilePath);
1918
+ if (remapped) {
1919
+ const content = await tryRead(remapped);
1920
+ if (content)
1921
+ return content;
1922
+ }
1923
+ }
1924
+ // 2. Filesystem scan using model from config
1925
+ const diskPath = await findD365FileOnDisk('form', baseFormName);
1926
+ if (diskPath)
1927
+ return tryRead(diskPath);
1928
+ return null;
1929
+ }
1930
+ /**
1931
+ * Resolve a possibly-fuzzy `parentControl` value to the exact control name in the base form.
1932
+ *
1933
+ * Returns:
1934
+ * { resolved, pathStr } — unique case-insensitive substring match (use this name)
1935
+ * { multiple } — ambiguous (return candidates to caller)
1936
+ * null — form not found or no controls matched; caller uses original value
1937
+ */
1938
+ async function resolveParentControl(extensionObjectName, parentControlQuery, symbolIndex, explicitBaseFormName) {
1939
+ // Base form name: "CustTable.MyExt" → "CustTable"
1940
+ const baseFormName = explicitBaseFormName || extensionObjectName.split('.')[0];
1941
+ if (!baseFormName)
1942
+ return null;
1943
+ const xmlContent = await findBaseFormXml(baseFormName, symbolIndex);
1944
+ if (!xmlContent)
1945
+ return null;
1946
+ let xmlObj;
1947
+ try {
1948
+ xmlObj = await parseStringPromise(xmlContent);
1949
+ }
1950
+ catch {
1951
+ return null;
1952
+ }
1953
+ const all = allControlsFromFormXmlObj(xmlObj);
1954
+ const lq = parentControlQuery.toLowerCase();
1955
+ const matches = all.filter(c => c.name.toLowerCase().includes(lq));
1956
+ if (matches.length === 0)
1957
+ return null; // No match — caller proceeds with original
1958
+ if (matches.length === 1)
1959
+ return { resolved: matches[0].name, pathStr: matches[0].pathStr };
1960
+ // Multiple substring matches — try an exact case-insensitive match first
1961
+ const exact = matches.filter(c => c.name.toLowerCase() === lq);
1962
+ if (exact.length === 1)
1963
+ return { resolved: exact[0].name, pathStr: exact[0].pathStr };
1964
+ return { multiple: matches };
1965
+ }
1966
+ // Tool registration (name, description, inputSchema) lives inline in
1967
+ // src/server/mcpServer.ts - the single source of truth for tool instructions.
1968
+ /**
1969
+ * Resolve the primitive base type for an EDT by walking the edt_metadata chain.
1970
+ * Used by add-field to auto-fill fieldBaseType when the caller omits it.
1971
+ * A newly-created EDT not yet in the index returns the EDT name so the bridge
1972
+ * can still apply its own name-based heuristics.
1973
+ */
1974
+ function resolveEdtBaseTypeForField(edtName, db, depth = 0) {
1975
+ const PRIMITIVES = new Set([
1976
+ 'String', 'Integer', 'Int64', 'Real', 'Date', 'UtcDateTime', 'DateTime',
1977
+ 'Enum', 'Container', 'Guid', 'GUID',
1978
+ ]);
1979
+ if (depth > 8)
1980
+ return edtName;
1981
+ if (PRIMITIVES.has(edtName))
1982
+ return edtName;
1983
+ try {
1984
+ const row = db.prepare(`SELECT extends, enum_type FROM edt_metadata WHERE edt_name = ? LIMIT 1`).get(edtName);
1985
+ if (!row)
1986
+ return edtName; // not yet indexed — let bridge use name heuristics
1987
+ if (row.enum_type && !row.extends)
1988
+ return 'Enum';
1989
+ if (!row.extends)
1990
+ return edtName;
1991
+ if (PRIMITIVES.has(row.extends))
1992
+ return row.extends;
1993
+ return resolveEdtBaseTypeForField(row.extends, db, depth + 1);
1994
+ }
1995
+ catch {
1996
+ return edtName;
1997
+ }
1998
+ }
1999
+ /** Lower-case the first character — D365FO convention for a table buffer variable. */
2000
+ function bufferVarName(tableName) {
2001
+ return tableName.charAt(0).toLowerCase() + tableName.slice(1);
2002
+ }
2003
+ /**
2004
+ * Resolve the EDT/type of a table field from the symbol index so generated
2005
+ * find/exist signatures use the correct parameter type. The field's `signature`
2006
+ * column stores its EDT name. Returns null when the field is not indexed.
2007
+ */
2008
+ /** Metadata base-type keywords (the stripped i:type, e.g. AxTableFieldString → "String").
2009
+ * These are NOT valid X++ parameter types — a field's signature in the index stores
2010
+ * the base type, not its EDT, so a resolved value matching one of these must be
2011
+ * discarded in favour of the field name (D365 fields are conventionally named after
2012
+ * their EDT, e.g. field ItemId uses EDT ItemId). */
2013
+ const METADATA_BASE_TYPE_KEYWORDS = new Set([
2014
+ 'string', 'integer', 'int', 'int64', 'real', 'date', 'time', 'datetime',
2015
+ 'utcdatetime', 'guid', 'container', 'enum', 'boolean', 'memo',
2016
+ ]);
2017
+ function isMetadataBaseTypeKeyword(t) {
2018
+ return METADATA_BASE_TYPE_KEYWORDS.has(t.trim().toLowerCase());
2019
+ }
2020
+ function resolveFieldEdt(tableName, fieldName, db) {
2021
+ try {
2022
+ // Canonicalize the parent — `parent_name = ? COLLATE NOCASE` cannot use
2023
+ // idx_parent_type_name and scans all 360k field rows (180 s cold). NOCASE
2024
+ // on the field name is fine inside the indexed parent+type range.
2025
+ const canonical = lookupSymbolNocase(db, tableName)?.name ?? tableName;
2026
+ const row = db.prepare(`SELECT signature FROM symbols WHERE type = 'field' AND parent_name = ? AND name = ? COLLATE NOCASE LIMIT 1`).get(canonical, fieldName);
2027
+ const sig = row?.signature?.trim();
2028
+ // The index stores the field's BASE TYPE (e.g. "String"), not its EDT. A base-type
2029
+ // keyword is not a usable X++ parameter type, so treat it as unresolved — the caller
2030
+ // then falls back to the field name, which is conventionally the EDT.
2031
+ if (!sig || isMetadataBaseTypeKeyword(sig))
2032
+ return null;
2033
+ return sig;
2034
+ }
2035
+ catch {
2036
+ return null;
2037
+ }
2038
+ }
2039
+ /**
2040
+ * Generate idiomatic X++ source for a standard table method from a high-level
2041
+ * `tableMethodType`. The method name is implied by the type (find/exist/…), so
2042
+ * callers need not pass methodName or sourceCode — only tableMethodType (plus
2043
+ * tableKeyField for find/exist).
2044
+ *
2045
+ * Returns the generated method name, source, and an optional advisory note
2046
+ * (e.g. when the key field's EDT could not be resolved from the index).
2047
+ */
2048
+ export function generateTableMethodSource(tableName, methodType, keyField, db) {
2049
+ const buf = bufferVarName(tableName);
2050
+ switch (methodType) {
2051
+ case 'find':
2052
+ case 'exist': {
2053
+ if (!keyField) {
2054
+ throw new Error(`add-table-method with tableMethodType="${methodType}" requires tableKeyField ` +
2055
+ `(the primary key field to select on, e.g. tableKeyField="ItemId").`);
2056
+ }
2057
+ const resolvedEdt = resolveFieldEdt(tableName, keyField, db);
2058
+ const paramType = resolvedEdt ?? keyField;
2059
+ const note = resolvedEdt
2060
+ ? undefined
2061
+ : `⚠️ Could not resolve the EDT for field "${keyField}" on "${tableName}" from the index — ` +
2062
+ `the parameter type defaulted to "${keyField}". Verify it compiles, or pass full sourceCode instead.`;
2063
+ const param = `_${bufferVarName(keyField)}`;
2064
+ if (methodType === 'find') {
2065
+ const source = `/// <summary>\n` +
2066
+ `/// Finds the <c>${tableName}</c> record for the given ${keyField}.\n` +
2067
+ `/// </summary>\n` +
2068
+ `/// <param name = "${param}">The ${keyField} value to find.</param>\n` +
2069
+ `/// <param name = "_forUpdate">Select the record for update; optional.</param>\n` +
2070
+ `/// <returns>The matching <c>${tableName}</c> record; an empty buffer if none exists.</returns>\n` +
2071
+ `public static ${tableName} find(${paramType} ${param}, boolean _forUpdate = false)\n` +
2072
+ `{\n` +
2073
+ ` ${tableName} ${buf};\n` +
2074
+ `\n` +
2075
+ ` if (${param})\n` +
2076
+ ` {\n` +
2077
+ ` ${buf}.selectForUpdate(_forUpdate);\n` +
2078
+ `\n` +
2079
+ ` select firstonly ${buf}\n` +
2080
+ ` where ${buf}.${keyField} == ${param};\n` +
2081
+ ` }\n` +
2082
+ `\n` +
2083
+ ` return ${buf};\n` +
2084
+ `}`;
2085
+ return { methodName: 'find', source, note };
2086
+ }
2087
+ // exist
2088
+ const source = `/// <summary>\n` +
2089
+ `/// Checks whether a <c>${tableName}</c> record exists for the given ${keyField}.\n` +
2090
+ `/// </summary>\n` +
2091
+ `/// <param name = "${param}">The ${keyField} value to check.</param>\n` +
2092
+ `/// <returns>true if a matching record exists; otherwise false.</returns>\n` +
2093
+ `public static boolean exist(${paramType} ${param})\n` +
2094
+ `{\n` +
2095
+ ` return ${param} &&\n` +
2096
+ ` (select firstonly RecId from ${buf}\n` +
2097
+ ` where ${buf}.${keyField} == ${param}).RecId != 0;\n` +
2098
+ `}`;
2099
+ return { methodName: 'exist', source, note };
2100
+ }
2101
+ case 'findByRecId': {
2102
+ const source = `/// <summary>\n` +
2103
+ `/// Finds the <c>${tableName}</c> record for the given RecId.\n` +
2104
+ `/// </summary>\n` +
2105
+ `/// <param name = "_recId">The RecId to find.</param>\n` +
2106
+ `/// <param name = "_forUpdate">Select the record for update; optional.</param>\n` +
2107
+ `/// <returns>The matching <c>${tableName}</c> record; an empty buffer if none exists.</returns>\n` +
2108
+ `public static ${tableName} findByRecId(RefRecId _recId, boolean _forUpdate = false)\n` +
2109
+ `{\n` +
2110
+ ` ${tableName} ${buf};\n` +
2111
+ `\n` +
2112
+ ` if (_recId)\n` +
2113
+ ` {\n` +
2114
+ ` ${buf}.selectForUpdate(_forUpdate);\n` +
2115
+ `\n` +
2116
+ ` select firstonly ${buf}\n` +
2117
+ ` where ${buf}.RecId == _recId;\n` +
2118
+ ` }\n` +
2119
+ `\n` +
2120
+ ` return ${buf};\n` +
2121
+ `}`;
2122
+ return { methodName: 'findByRecId', source };
2123
+ }
2124
+ case 'validateWrite':
2125
+ case 'validateDelete': {
2126
+ const source = `/// <summary>\n` +
2127
+ `/// Validates the record before ${methodType === 'validateWrite' ? 'writing' : 'deletion'}.\n` +
2128
+ `/// </summary>\n` +
2129
+ `/// <returns>true if the record is valid; otherwise false.</returns>\n` +
2130
+ `public boolean ${methodType}()\n` +
2131
+ `{\n` +
2132
+ ` boolean ret;\n` +
2133
+ `\n` +
2134
+ ` ret = super();\n` +
2135
+ `\n` +
2136
+ ` // TODO: add custom ${methodType} validation\n` +
2137
+ `\n` +
2138
+ ` return ret;\n` +
2139
+ `}`;
2140
+ return { methodName: methodType, source };
2141
+ }
2142
+ case 'initValue': {
2143
+ const source = `/// <summary>\n` +
2144
+ `/// Initializes the record with default field values.\n` +
2145
+ `/// </summary>\n` +
2146
+ `public void initValue()\n` +
2147
+ `{\n` +
2148
+ ` super();\n` +
2149
+ `\n` +
2150
+ ` // TODO: set default field values\n` +
2151
+ `}`;
2152
+ return { methodName: 'initValue', source };
2153
+ }
2154
+ default: {
2155
+ // Exhaustiveness guard — the schema enum should keep this unreachable.
2156
+ throw new Error(`Unsupported tableMethodType: ${methodType}`);
2157
+ }
2158
+ }
2159
+ }
2160
+ /**
2161
+ * Generate an X++ display method stub returning the given EDT/type.
2162
+ * Used when add-display-method is called with displayMethodReturnEdt but no
2163
+ * explicit sourceCode/methodCode.
2164
+ */
2165
+ export function generateDisplayMethodSource(methodName, returnEdt) {
2166
+ return (`/// <summary>\n` +
2167
+ `/// Display method returning <c>${returnEdt}</c>.\n` +
2168
+ `/// </summary>\n` +
2169
+ `/// <returns>The computed ${returnEdt} value.</returns>\n` +
2170
+ `public display ${returnEdt} ${methodName}()\n` +
2171
+ `{\n` +
2172
+ ` ${returnEdt} ret;\n` +
2173
+ `\n` +
2174
+ ` // TODO: compute the display value\n` +
2175
+ `\n` +
2176
+ ` return ret;\n` +
2177
+ `}`);
2178
+ }
2179
+ //# sourceMappingURL=modifyD365File.js.map