@aryacode/dsh-engineering 0.0.0-stage → 0.3.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 (281) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/LICENSE +21 -0
  3. package/README.md +228 -2
  4. package/cordis.patch.yml +122 -0
  5. package/locale/en.json +6 -0
  6. package/locale/zh.json +6 -0
  7. package/package.json +77 -3
  8. package/vendor/ftview/README.md +55 -0
  9. package/vendor/ftview/SKILL.md +74 -0
  10. package/vendor/ftview/lib/checks.d.ts +27 -0
  11. package/vendor/ftview/lib/checks.js +39 -0
  12. package/vendor/ftview/lib/core.d.ts +208 -0
  13. package/vendor/ftview/lib/core.js +160 -0
  14. package/vendor/ftview/lib/edit/drafts.d.ts +49 -0
  15. package/vendor/ftview/lib/edit/drafts.js +84 -0
  16. package/vendor/ftview/lib/edit/operations.d.ts +62 -0
  17. package/vendor/ftview/lib/edit/operations.js +126 -0
  18. package/vendor/ftview/lib/edit/prepare.d.ts +38 -0
  19. package/vendor/ftview/lib/edit/prepare.js +133 -0
  20. package/vendor/ftview/lib/edit/tools.d.ts +25 -0
  21. package/vendor/ftview/lib/edit/tools.js +198 -0
  22. package/vendor/ftview/lib/edit.d.ts +30 -0
  23. package/vendor/ftview/lib/edit.js +38 -0
  24. package/vendor/ftview/lib/index.d.ts +31 -0
  25. package/vendor/ftview/lib/index.js +36 -0
  26. package/vendor/ftview/lib/model.d.ts +80 -0
  27. package/vendor/ftview/lib/model.js +267 -0
  28. package/vendor/ftview/lib/session.d.ts +48 -0
  29. package/vendor/ftview/lib/session.js +98 -0
  30. package/vendor/ftview/lib/skill.d.ts +29 -0
  31. package/vendor/ftview/lib/skill.js +43 -0
  32. package/vendor/ftview/lib/studio/discover.d.ts +36 -0
  33. package/vendor/ftview/lib/studio/discover.js +87 -0
  34. package/vendor/ftview/lib/studio/preflight.d.ts +51 -0
  35. package/vendor/ftview/lib/studio/preflight.js +64 -0
  36. package/vendor/ftview/lib/studio/tools.d.ts +15 -0
  37. package/vendor/ftview/lib/studio/tools.js +83 -0
  38. package/vendor/ftview/lib/studio.d.ts +30 -0
  39. package/vendor/ftview/lib/studio.js +37 -0
  40. package/vendor/ftview/lib/tools.d.ts +14 -0
  41. package/vendor/ftview/lib/tools.js +202 -0
  42. package/vendor/l5x/README.md +62 -0
  43. package/vendor/l5x/SKILL.md +81 -0
  44. package/vendor/l5x/lib/approvals.d.ts +50 -0
  45. package/vendor/l5x/lib/approvals.js +93 -0
  46. package/vendor/l5x/lib/client.js +412 -0
  47. package/vendor/l5x/lib/client.js.map +1 -0
  48. package/vendor/l5x/lib/core/errors.d.ts +38 -0
  49. package/vendor/l5x/lib/core/errors.js +48 -0
  50. package/vendor/l5x/lib/core/params.d.ts +49 -0
  51. package/vendor/l5x/lib/core/params.js +39 -0
  52. package/vendor/l5x/lib/core/result.d.ts +46 -0
  53. package/vendor/l5x/lib/core/result.js +21 -0
  54. package/vendor/l5x/lib/core/tool.d.ts +102 -0
  55. package/vendor/l5x/lib/core/tool.js +19 -0
  56. package/vendor/l5x/lib/drafts.d.ts +66 -0
  57. package/vendor/l5x/lib/drafts.js +98 -0
  58. package/vendor/l5x/lib/editing.d.ts +30 -0
  59. package/vendor/l5x/lib/editing.js +42 -0
  60. package/vendor/l5x/lib/engine/acd-edit.d.ts +13 -0
  61. package/vendor/l5x/lib/engine/acd-edit.js +115 -0
  62. package/vendor/l5x/lib/engine/analysis.d.ts +22 -0
  63. package/vendor/l5x/lib/engine/analysis.js +258 -0
  64. package/vendor/l5x/lib/engine/capabilities.d.ts +9 -0
  65. package/vendor/l5x/lib/engine/capabilities.js +40 -0
  66. package/vendor/l5x/lib/engine/change-set.d.ts +122 -0
  67. package/vendor/l5x/lib/engine/change-set.js +446 -0
  68. package/vendor/l5x/lib/engine/configuration.d.ts +21 -0
  69. package/vendor/l5x/lib/engine/configuration.js +42 -0
  70. package/vendor/l5x/lib/engine/details.d.ts +47 -0
  71. package/vendor/l5x/lib/engine/details.js +173 -0
  72. package/vendor/l5x/lib/engine/direct-polling.d.ts +91 -0
  73. package/vendor/l5x/lib/engine/direct-polling.js +193 -0
  74. package/vendor/l5x/lib/engine/download-review.d.ts +15 -0
  75. package/vendor/l5x/lib/engine/download-review.js +63 -0
  76. package/vendor/l5x/lib/engine/logic-syntax.d.ts +60 -0
  77. package/vendor/l5x/lib/engine/logic-syntax.js +237 -0
  78. package/vendor/l5x/lib/engine/observations.d.ts +41 -0
  79. package/vendor/l5x/lib/engine/observations.js +67 -0
  80. package/vendor/l5x/lib/engine/project-analysis.d.ts +131 -0
  81. package/vendor/l5x/lib/engine/project-analysis.js +314 -0
  82. package/vendor/l5x/lib/engine/review-evidence.d.ts +8 -0
  83. package/vendor/l5x/lib/engine/review-evidence.js +49 -0
  84. package/vendor/l5x/lib/engine/review.d.ts +18 -0
  85. package/vendor/l5x/lib/engine/review.js +123 -0
  86. package/vendor/l5x/lib/engine/studio-sdk.d.ts +101 -0
  87. package/vendor/l5x/lib/engine/studio-sdk.js +571 -0
  88. package/vendor/l5x/lib/engine/tag-resolution.d.ts +20 -0
  89. package/vendor/l5x/lib/engine/tag-resolution.js +169 -0
  90. package/vendor/l5x/lib/engine/usage.d.ts +16 -0
  91. package/vendor/l5x/lib/engine/usage.js +92 -0
  92. package/vendor/l5x/lib/engine/xml-regions.d.ts +23 -0
  93. package/vendor/l5x/lib/engine/xml-regions.js +77 -0
  94. package/vendor/l5x/lib/fs.d.ts +17 -0
  95. package/vendor/l5x/lib/fs.js +36 -0
  96. package/vendor/l5x/lib/index.d.ts +36 -0
  97. package/vendor/l5x/lib/index.js +42 -0
  98. package/vendor/l5x/lib/live.d.ts +48 -0
  99. package/vendor/l5x/lib/live.js +48 -0
  100. package/vendor/l5x/lib/model/aois.d.ts +39 -0
  101. package/vendor/l5x/lib/model/aois.js +112 -0
  102. package/vendor/l5x/lib/model/errors.d.ts +15 -0
  103. package/vendor/l5x/lib/model/errors.js +18 -0
  104. package/vendor/l5x/lib/model/index.d.ts +20 -0
  105. package/vendor/l5x/lib/model/index.js +19 -0
  106. package/vendor/l5x/lib/model/module.d.ts +37 -0
  107. package/vendor/l5x/lib/model/module.js +54 -0
  108. package/vendor/l5x/lib/model/modules.d.ts +24 -0
  109. package/vendor/l5x/lib/model/modules.js +55 -0
  110. package/vendor/l5x/lib/model/parse.d.ts +32 -0
  111. package/vendor/l5x/lib/model/parse.js +116 -0
  112. package/vendor/l5x/lib/model/project.d.ts +15 -0
  113. package/vendor/l5x/lib/model/project.js +31 -0
  114. package/vendor/l5x/lib/model/routines.d.ts +71 -0
  115. package/vendor/l5x/lib/model/routines.js +183 -0
  116. package/vendor/l5x/lib/model/tags.d.ts +51 -0
  117. package/vendor/l5x/lib/model/tags.js +119 -0
  118. package/vendor/l5x/lib/model/types.d.ts +304 -0
  119. package/vendor/l5x/lib/model/types.js +12 -0
  120. package/vendor/l5x/lib/model/udts.d.ts +34 -0
  121. package/vendor/l5x/lib/model/udts.js +81 -0
  122. package/vendor/l5x/lib/model/xml.d.ts +139 -0
  123. package/vendor/l5x/lib/model/xml.js +291 -0
  124. package/vendor/l5x/lib/model/xref.d.ts +28 -0
  125. package/vendor/l5x/lib/model/xref.js +154 -0
  126. package/vendor/l5x/lib/session.d.ts +119 -0
  127. package/vendor/l5x/lib/session.js +236 -0
  128. package/vendor/l5x/lib/skill.d.ts +30 -0
  129. package/vendor/l5x/lib/skill.js +44 -0
  130. package/vendor/l5x/lib/studio-state.d.ts +77 -0
  131. package/vendor/l5x/lib/studio-state.js +79 -0
  132. package/vendor/l5x/lib/studio.d.ts +29 -0
  133. package/vendor/l5x/lib/studio.js +40 -0
  134. package/vendor/l5x/lib/tools/checks.d.ts +19 -0
  135. package/vendor/l5x/lib/tools/checks.js +140 -0
  136. package/vendor/l5x/lib/tools/common.d.ts +41 -0
  137. package/vendor/l5x/lib/tools/common.js +84 -0
  138. package/vendor/l5x/lib/tools/controller.d.ts +21 -0
  139. package/vendor/l5x/lib/tools/controller.js +170 -0
  140. package/vendor/l5x/lib/tools/drafts.d.ts +23 -0
  141. package/vendor/l5x/lib/tools/drafts.js +270 -0
  142. package/vendor/l5x/lib/tools/live.d.ts +21 -0
  143. package/vendor/l5x/lib/tools/live.js +175 -0
  144. package/vendor/l5x/lib/tools/read.d.ts +19 -0
  145. package/vendor/l5x/lib/tools/read.js +245 -0
  146. package/vendor/l5x/lib/tools/references.d.ts +20 -0
  147. package/vendor/l5x/lib/tools/references.js +269 -0
  148. package/vendor/l5x/lib/tools/studio.d.ts +21 -0
  149. package/vendor/l5x/lib/tools/studio.js +193 -0
  150. package/vendor/l5x/poll-worker.mjs +77 -0
  151. package/vendor/l5x/studio-bridge/NuGet.Config +1 -0
  152. package/vendor/l5x/studio-bridge/Program.cs +304 -0
  153. package/vendor/l5x/studio-bridge/StudioBridge.csproj +11 -0
  154. package/vendor/mwi/README.md +60 -0
  155. package/vendor/mwi/SKILL.md +87 -0
  156. package/vendor/mwi/bridge/README.md +8 -0
  157. package/vendor/mwi/bridge/mw_bridge.ps1 +2763 -0
  158. package/vendor/mwi/bridge/sdk-interop-importer.cs +30 -0
  159. package/vendor/mwi/bridge/sdk-variable-reader.cs +22 -0
  160. package/vendor/mwi/bridge/start_bridge.cmd +21 -0
  161. package/vendor/mwi/bridge/watch_dialogs.cmd +13 -0
  162. package/vendor/mwi/bridge/watch_dialogs.ps1 +153 -0
  163. package/vendor/mwi/code/engine/motionworks_iec_mcp/__init__.py +14 -0
  164. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
  165. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/block_interfaces.cpython-312.pyc +0 -0
  166. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/cfb.cpython-312.pyc +0 -0
  167. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/datatypes.cpython-312.pyc +0 -0
  168. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/declarations.cpython-312.pyc +0 -0
  169. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/errors.cpython-312.pyc +0 -0
  170. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/graphical_listing.cpython-312.pyc +0 -0
  171. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/grid.cpython-312.pyc +0 -0
  172. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/knowledge.cpython-312.pyc +0 -0
  173. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/mwt_bind.cpython-312.pyc +0 -0
  174. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/navigation.cpython-312.pyc +0 -0
  175. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/program_checks.cpython-312.pyc +0 -0
  176. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/project.cpython-312.pyc +0 -0
  177. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/staging.cpython-312.pyc +0 -0
  178. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/structure.cpython-312.pyc +0 -0
  179. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/task_bindings.cpython-312.pyc +0 -0
  180. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tasks.cpython-312.pyc +0 -0
  181. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tree.cpython-312.pyc +0 -0
  182. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/validation.cpython-312.pyc +0 -0
  183. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variable_edit.cpython-312.pyc +0 -0
  184. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variables.cpython-312.pyc +0 -0
  185. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/workflow.cpython-312.pyc +0 -0
  186. package/vendor/mwi/code/engine/motionworks_iec_mcp/block_interfaces.py +275 -0
  187. package/vendor/mwi/code/engine/motionworks_iec_mcp/build.py +339 -0
  188. package/vendor/mwi/code/engine/motionworks_iec_mcp/capture.py +448 -0
  189. package/vendor/mwi/code/engine/motionworks_iec_mcp/cfb.py +765 -0
  190. package/vendor/mwi/code/engine/motionworks_iec_mcp/com.py +268 -0
  191. package/vendor/mwi/code/engine/motionworks_iec_mcp/datatypes.py +204 -0
  192. package/vendor/mwi/code/engine/motionworks_iec_mcp/declarations.py +399 -0
  193. package/vendor/mwi/code/engine/motionworks_iec_mcp/descriptions.py +30 -0
  194. package/vendor/mwi/code/engine/motionworks_iec_mcp/eip.py +292 -0
  195. package/vendor/mwi/code/engine/motionworks_iec_mcp/errors.py +36 -0
  196. package/vendor/mwi/code/engine/motionworks_iec_mcp/graphical_listing.py +208 -0
  197. package/vendor/mwi/code/engine/motionworks_iec_mcp/grid.py +227 -0
  198. package/vendor/mwi/code/engine/motionworks_iec_mcp/ide.py +317 -0
  199. package/vendor/mwi/code/engine/motionworks_iec_mcp/iec.py +317 -0
  200. package/vendor/mwi/code/engine/motionworks_iec_mcp/installed_help.py +151 -0
  201. package/vendor/mwi/code/engine/motionworks_iec_mcp/knowledge.py +218 -0
  202. package/vendor/mwi/code/engine/motionworks_iec_mcp/libraries.py +250 -0
  203. package/vendor/mwi/code/engine/motionworks_iec_mcp/manuals.py +520 -0
  204. package/vendor/mwi/code/engine/motionworks_iec_mcp/mwt_bind.py +133 -0
  205. package/vendor/mwi/code/engine/motionworks_iec_mcp/navigation.py +44 -0
  206. package/vendor/mwi/code/engine/motionworks_iec_mcp/pou_writer.py +1547 -0
  207. package/vendor/mwi/code/engine/motionworks_iec_mcp/program_checks.py +360 -0
  208. package/vendor/mwi/code/engine/motionworks_iec_mcp/project.py +704 -0
  209. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/catalog.json +672 -0
  210. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/patterns.json +86 -0
  211. package/vendor/mwi/code/engine/motionworks_iec_mcp/restore.py +208 -0
  212. package/vendor/mwi/code/engine/motionworks_iec_mcp/server.py +1913 -0
  213. package/vendor/mwi/code/engine/motionworks_iec_mcp/snapshot.py +138 -0
  214. package/vendor/mwi/code/engine/motionworks_iec_mcp/st_comments.py +25 -0
  215. package/vendor/mwi/code/engine/motionworks_iec_mcp/staging.py +179 -0
  216. package/vendor/mwi/code/engine/motionworks_iec_mcp/stlint.py +676 -0
  217. package/vendor/mwi/code/engine/motionworks_iec_mcp/structure.py +98 -0
  218. package/vendor/mwi/code/engine/motionworks_iec_mcp/task_bindings.py +68 -0
  219. package/vendor/mwi/code/engine/motionworks_iec_mcp/tasks.py +162 -0
  220. package/vendor/mwi/code/engine/motionworks_iec_mcp/transaction.py +157 -0
  221. package/vendor/mwi/code/engine/motionworks_iec_mcp/tree.py +563 -0
  222. package/vendor/mwi/code/engine/motionworks_iec_mcp/tree_writer.py +790 -0
  223. package/vendor/mwi/code/engine/motionworks_iec_mcp/uia.py +465 -0
  224. package/vendor/mwi/code/engine/motionworks_iec_mcp/validation.py +41 -0
  225. package/vendor/mwi/code/engine/motionworks_iec_mcp/variable_edit.py +183 -0
  226. package/vendor/mwi/code/engine/motionworks_iec_mcp/variables.py +472 -0
  227. package/vendor/mwi/code/engine/motionworks_iec_mcp/workflow.py +130 -0
  228. package/vendor/mwi/code/engine/motionworks_iec_mcp/writer.py +666 -0
  229. package/vendor/mwi/code/mw_code.py +1258 -0
  230. package/vendor/mwi/legacy/edit-session.js +50 -0
  231. package/vendor/mwi/legacy/fb-insertion.js +74 -0
  232. package/vendor/mwi/legacy/native-code.js +83 -0
  233. package/vendor/mwi/legacy/native-groups.js +75 -0
  234. package/vendor/mwi/legacy/native-structure.js +143 -0
  235. package/vendor/mwi/legacy/native-variables.js +77 -0
  236. package/vendor/mwi/legacy/pou-conversion.js +47 -0
  237. package/vendor/mwi/legacy/pou-package.js +66 -0
  238. package/vendor/mwi/lib/core/bridge.d.ts +71 -0
  239. package/vendor/mwi/lib/core/bridge.js +53 -0
  240. package/vendor/mwi/lib/core/code-engine.d.ts +47 -0
  241. package/vendor/mwi/lib/core/code-engine.js +109 -0
  242. package/vendor/mwi/lib/core/errors.d.ts +37 -0
  243. package/vendor/mwi/lib/core/errors.js +47 -0
  244. package/vendor/mwi/lib/core/params.d.ts +52 -0
  245. package/vendor/mwi/lib/core/params.js +46 -0
  246. package/vendor/mwi/lib/core/ps-bridge.d.ts +62 -0
  247. package/vendor/mwi/lib/core/ps-bridge.js +127 -0
  248. package/vendor/mwi/lib/core/result.d.ts +45 -0
  249. package/vendor/mwi/lib/core/result.js +20 -0
  250. package/vendor/mwi/lib/core/tool.d.ts +80 -0
  251. package/vendor/mwi/lib/core/tool.js +55 -0
  252. package/vendor/mwi/lib/edit/compare.d.ts +33 -0
  253. package/vendor/mwi/lib/edit/compare.js +70 -0
  254. package/vendor/mwi/lib/edit.d.ts +20 -0
  255. package/vendor/mwi/lib/edit.js +32 -0
  256. package/vendor/mwi/lib/index.d.ts +21 -0
  257. package/vendor/mwi/lib/index.js +33 -0
  258. package/vendor/mwi/lib/read/service.d.ts +117 -0
  259. package/vendor/mwi/lib/read/service.js +229 -0
  260. package/vendor/mwi/lib/read.d.ts +21 -0
  261. package/vendor/mwi/lib/read.js +33 -0
  262. package/vendor/mwi/lib/session/project.d.ts +34 -0
  263. package/vendor/mwi/lib/session/project.js +54 -0
  264. package/vendor/mwi/lib/session/service.d.ts +167 -0
  265. package/vendor/mwi/lib/session/service.js +566 -0
  266. package/vendor/mwi/lib/session/stage.d.ts +84 -0
  267. package/vendor/mwi/lib/session/stage.js +187 -0
  268. package/vendor/mwi/lib/session/workspace.d.ts +52 -0
  269. package/vendor/mwi/lib/session/workspace.js +90 -0
  270. package/vendor/mwi/lib/skill.d.ts +36 -0
  271. package/vendor/mwi/lib/skill.js +56 -0
  272. package/vendor/mwi/lib/tools/edit.d.ts +20 -0
  273. package/vendor/mwi/lib/tools/edit.js +286 -0
  274. package/vendor/mwi/lib/tools/read.d.ts +19 -0
  275. package/vendor/mwi/lib/tools/read.js +161 -0
  276. package/vendor/mwi/lib/tools/session.d.ts +20 -0
  277. package/vendor/mwi/lib/tools/session.js +166 -0
  278. package/vendor/mwi/lib/tools/verify.d.ts +19 -0
  279. package/vendor/mwi/lib/tools/verify.js +85 -0
  280. package/vendor/mwi/lib/verify.d.ts +17 -0
  281. package/vendor/mwi/lib/verify.js +29 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The rebuilt FactoryTalk View ME read surface.
3
+ *
4
+ * Six intents over one opened export folder: open, list, get, find, check and
5
+ * compare. No Python engine, no Studio automation, no MER decoding.
6
+ *
7
+ * @module dsh-engineering-ftview
8
+ */
9
+ import { type PluginContext } from './core.ts';
10
+ /** Plugin name in a composition. */
11
+ export declare const name = "ftview-read";
12
+ /** Services this plugin cannot register without. */
13
+ export declare const inject: readonly ["tools", "fs"];
14
+ /** Service name the editing entry consumes. */
15
+ export declare const VIEWS_SERVICE = "ftviewViews";
16
+ /** Configuration for the read surface. */
17
+ export interface Config {
18
+ /** Most display and tag files one open may scan. Defaults to 2000. */
19
+ maxFiles?: number;
20
+ }
21
+ /**
22
+ * Register the ME read surface.
23
+ *
24
+ * @param ctx - Plugin context with the tool registry and filesystem provider.
25
+ * @param config - Deployment limits.
26
+ */
27
+ export declare function apply(ctx: PluginContext, config?: Config): void;
28
+ export { ViewSession } from './session.ts';
29
+ export { ftviewTools } from './tools.ts';
30
+ export { EngineeringError } from './core.ts';
31
+ export type { Coverage, NextStep, Success, ToolDefinition } from './core.ts';
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The rebuilt FactoryTalk View ME read surface.
3
+ *
4
+ * Six intents over one opened export folder: open, list, get, find, check and
5
+ * compare. No Python engine, no Studio automation, no MER decoding.
6
+ *
7
+ * @module dsh-engineering-ftview
8
+ */
9
+ import { stringFs } from "./core.js";
10
+ import { ViewSession } from "./session.js";
11
+ import { ftviewTools } from "./tools.js";
12
+ /** Plugin name in a composition. */
13
+ export const name = 'ftview-read';
14
+ /** Services this plugin cannot register without. */
15
+ export const inject = ['tools', 'fs'];
16
+ /** Service name the editing entry consumes. */
17
+ export const VIEWS_SERVICE = 'ftviewViews';
18
+ /**
19
+ * Register the ME read surface.
20
+ *
21
+ * @param ctx - Plugin context with the tool registry and filesystem provider.
22
+ * @param config - Deployment limits.
23
+ */
24
+ export function apply(ctx, config = {}) {
25
+ const maxFiles = config.maxFiles ?? 2000;
26
+ if (!Number.isInteger(maxFiles) || maxFiles <= 0) {
27
+ throw new Error('ftview-read: maxFiles must be a positive integer');
28
+ }
29
+ const session = new ViewSession(stringFs(ctx.fs), maxFiles);
30
+ ctx.provide?.(VIEWS_SERVICE, session);
31
+ for (const definition of ftviewTools(session))
32
+ ctx.tools.register(definition);
33
+ }
34
+ export { ViewSession } from "./session.js";
35
+ export { ftviewTools } from "./tools.js";
36
+ export { EngineeringError } from "./core.js";
@@ -0,0 +1,80 @@
1
+ /**
2
+ * FactoryTalk View ME export scanning and parsing.
3
+ *
4
+ * Only documented text exports are read: display XML and the semicolon-separated
5
+ * HMI tag CSV. Native MER/MED databases are not decoded or rewritten. A display
6
+ * contributes its root identity and its named objects; the tag CSV contributes
7
+ * typed rows, and its structural problems are reported rather than hidden.
8
+ *
9
+ * @module dsh-engineering-ftview/model
10
+ */
11
+ import { type FsService } from './core.ts';
12
+ /** One row of the HMI tag CSV, keyed by its canonical columns. */
13
+ export interface TagRow {
14
+ readonly name: string;
15
+ readonly description: string;
16
+ readonly type: string;
17
+ readonly raw: Readonly<Record<string, string>>;
18
+ }
19
+ /** One named object inside a display. */
20
+ export interface DisplayObject {
21
+ readonly type: string;
22
+ readonly name?: string;
23
+ readonly attributes: Readonly<Record<string, string>>;
24
+ }
25
+ /** One parsed display file. */
26
+ export interface DisplayFile {
27
+ readonly file: string;
28
+ readonly name: string;
29
+ readonly objects: readonly DisplayObject[];
30
+ readonly parseError?: string;
31
+ }
32
+ /** One file the scan read, with its content hash. */
33
+ export interface ScannedFile {
34
+ readonly file: string;
35
+ readonly bytes: number;
36
+ readonly sha256: string;
37
+ }
38
+ /** One export folder, scanned once and read by every later call. */
39
+ export interface Application {
40
+ readonly root: string;
41
+ readonly name: string;
42
+ readonly displays: readonly DisplayFile[];
43
+ readonly tags: readonly TagRow[];
44
+ readonly tagFiles: readonly string[];
45
+ readonly tagErrors: readonly string[];
46
+ readonly diagnostics: readonly string[];
47
+ readonly files: readonly ScannedFile[];
48
+ }
49
+ /** Parsed tag CSV: rows plus the structural errors found while reading it. */
50
+ export interface TagCsv {
51
+ readonly rows: readonly TagRow[];
52
+ readonly errors: readonly string[];
53
+ }
54
+ /** Split one semicolon-separated CSV line, honouring double quotes. */
55
+ export declare function splitCsv(line: string): string[];
56
+ /**
57
+ * Parse the HMI tag CSV.
58
+ *
59
+ * @param text - Whole CSV file.
60
+ * @returns Rows plus structural errors; a file with no `###002` marker yields no rows.
61
+ */
62
+ export declare function parseTagCsv(text: string): TagCsv;
63
+ /**
64
+ * Parse one display XML file.
65
+ *
66
+ * @param file - Relative path inside the export folder.
67
+ * @param text - Whole XML file.
68
+ * @returns The display, or the parse error with a synthetic entry.
69
+ */
70
+ export declare function parseDisplay(file: string, text: string): DisplayFile;
71
+ /**
72
+ * Scan one export folder.
73
+ *
74
+ * @param fs - Filesystem provider.
75
+ * @param root - Resolved export folder.
76
+ * @param signal - Cancellation.
77
+ * @param maxFiles - Largest number of display/tag files accepted.
78
+ * @returns The scanned application.
79
+ */
80
+ export declare function scanApplication(fs: FsService, root: string, signal: AbortSignal | undefined, maxFiles: number): Promise<Application>;
@@ -0,0 +1,267 @@
1
+ /**
2
+ * FactoryTalk View ME export scanning and parsing.
3
+ *
4
+ * Only documented text exports are read: display XML and the semicolon-separated
5
+ * HMI tag CSV. Native MER/MED databases are not decoded or rewritten. A display
6
+ * contributes its root identity and its named objects; the tag CSV contributes
7
+ * typed rows, and its structural problems are reported rather than hidden.
8
+ *
9
+ * @module dsh-engineering-ftview/model
10
+ */
11
+ import { createHash } from 'node:crypto';
12
+ import { readdir } from 'node:fs/promises';
13
+ import { XMLParser } from 'fast-xml-parser';
14
+ import { EngineeringError } from "./core.js";
15
+ /** Attribute prefix the XML parser uses. */
16
+ const ATTRIBUTE = '@_';
17
+ /** Split one semicolon-separated CSV line, honouring double quotes. */
18
+ export function splitCsv(line) {
19
+ const cells = [];
20
+ let current = '';
21
+ let quoted = false;
22
+ for (let index = 0; index < line.length; index += 1) {
23
+ const character = line[index];
24
+ if (quoted) {
25
+ if (character === '"') {
26
+ if (line[index + 1] === '"') {
27
+ current += '"';
28
+ index += 1;
29
+ }
30
+ else {
31
+ quoted = false;
32
+ }
33
+ }
34
+ else {
35
+ current += character;
36
+ }
37
+ }
38
+ else if (character === '"') {
39
+ quoted = true;
40
+ }
41
+ else if (character === ';') {
42
+ cells.push(current);
43
+ current = '';
44
+ }
45
+ else {
46
+ current += character;
47
+ }
48
+ }
49
+ cells.push(current);
50
+ return cells;
51
+ }
52
+ /** Decode bytes as UTF-8, dropping a byte-order mark. */
53
+ function decode(bytes) {
54
+ const text = new TextDecoder('utf-8').decode(bytes);
55
+ return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
56
+ }
57
+ /**
58
+ * Parse the HMI tag CSV.
59
+ *
60
+ * @param text - Whole CSV file.
61
+ * @returns Rows plus structural errors; a file with no `###002` marker yields no rows.
62
+ */
63
+ export function parseTagCsv(text) {
64
+ const lines = text.split(/\r?\n/);
65
+ const versionLine = lines.findIndex(line => line.includes('###002'));
66
+ if (versionLine < 0) {
67
+ return { rows: [], errors: ['Unsupported tag CSV: the `###002` version line is missing.'] };
68
+ }
69
+ const headerIndex = lines.findIndex((line, index) => index > versionLine && line.trim() !== '');
70
+ if (headerIndex < 0) {
71
+ return { rows: [], errors: ['Unsupported tag CSV: no header row follows the version line.'] };
72
+ }
73
+ const header = splitCsv(lines[headerIndex]).map(cell => cell.trim());
74
+ const nameIndex = header.indexOf('Tag Name');
75
+ if (nameIndex < 0) {
76
+ return { rows: [], errors: ['Unsupported tag CSV: the header has no `Tag Name` column.'] };
77
+ }
78
+ const descriptionIndex = header.indexOf('Tag Description');
79
+ const typeIndex = header.indexOf('Tag Type');
80
+ const rows = [];
81
+ const errors = [];
82
+ const seen = new Set();
83
+ for (let index = headerIndex + 1; index < lines.length; index += 1) {
84
+ const line = lines[index];
85
+ if (line.trim() === '')
86
+ continue;
87
+ const cells = splitCsv(line);
88
+ if (cells.length < header.length) {
89
+ errors.push(`Row ${index + 1}: expected ${header.length} columns, found ${cells.length}.`);
90
+ continue;
91
+ }
92
+ const raw = {};
93
+ header.forEach((column, columnIndex) => {
94
+ if (column !== '')
95
+ raw[column] = (cells[columnIndex] ?? '').trim();
96
+ });
97
+ const name = (cells[nameIndex] ?? '').trim();
98
+ if (name === '') {
99
+ errors.push(`Row ${index + 1}: empty Tag Name.`);
100
+ continue;
101
+ }
102
+ if (seen.has(name))
103
+ errors.push(`Row ${index + 1}: duplicate tag name ${JSON.stringify(name)}.`);
104
+ seen.add(name);
105
+ rows.push({
106
+ name,
107
+ description: descriptionIndex >= 0 ? (cells[descriptionIndex] ?? '').trim() : '',
108
+ type: typeIndex >= 0 ? (cells[typeIndex] ?? '').trim() : '',
109
+ raw,
110
+ });
111
+ }
112
+ return { rows, errors };
113
+ }
114
+ /** The parser used for display XML, preserving attributes and order-independent names. */
115
+ const PARSER = new XMLParser({
116
+ ignoreAttributes: false,
117
+ attributeNamePrefix: ATTRIBUTE,
118
+ trimValues: true,
119
+ parseTagValue: false,
120
+ isArray: () => false,
121
+ });
122
+ /** Strip the attribute prefix from one attribute bag. */
123
+ function attributesOf(node) {
124
+ const attributes = {};
125
+ for (const [key, value] of Object.entries(node)) {
126
+ if (key.startsWith(ATTRIBUTE) && typeof value === 'string')
127
+ attributes[key.slice(ATTRIBUTE.length)] = value;
128
+ if (key.startsWith(ATTRIBUTE) && typeof value === 'number')
129
+ attributes[key.slice(ATTRIBUTE.length)] = String(value);
130
+ }
131
+ return attributes;
132
+ }
133
+ /** Collect one element and its descendants as display objects. */
134
+ function collectObjects(node, type, out, depth) {
135
+ if (depth > 8 || node === null || typeof node !== 'object' || Array.isArray(node))
136
+ return;
137
+ const record = node;
138
+ const attributes = attributesOf(record);
139
+ const named = attributes.name ?? attributes.Name;
140
+ out.push({ type, ...(named === undefined ? {} : { name: named }), attributes });
141
+ for (const [key, value] of Object.entries(record)) {
142
+ if (key.startsWith(ATTRIBUTE) || key === '#text')
143
+ continue;
144
+ if (Array.isArray(value)) {
145
+ for (const item of value)
146
+ collectObjects(item, key, out, depth + 1);
147
+ }
148
+ else {
149
+ collectObjects(value, key, out, depth + 1);
150
+ }
151
+ }
152
+ }
153
+ /**
154
+ * Parse one display XML file.
155
+ *
156
+ * @param file - Relative path inside the export folder.
157
+ * @param text - Whole XML file.
158
+ * @returns The display, or the parse error with a synthetic entry.
159
+ */
160
+ export function parseDisplay(file, text) {
161
+ const fallbackName = file.replace(/\\/gu, '/').split('/').pop()?.replace(/\.xml$/iu, '') ?? file;
162
+ try {
163
+ const parsed = PARSER.parse(text);
164
+ const rootKey = Object.keys(parsed).find(key => !key.startsWith('?'));
165
+ if (rootKey === undefined)
166
+ return { file, name: fallbackName, objects: [], parseError: 'The file has no root element.' };
167
+ const rootNode = parsed[rootKey];
168
+ if (!/display/iu.test(rootKey)) {
169
+ return { file, name: fallbackName, objects: [], parseError: `Root element ${rootKey} is not an ME display.` };
170
+ }
171
+ const root = (rootNode ?? {});
172
+ const attributes = attributesOf(root);
173
+ const objects = [];
174
+ for (const [key, value] of Object.entries(root)) {
175
+ if (key.startsWith(ATTRIBUTE) || key === '#text')
176
+ continue;
177
+ if (Array.isArray(value)) {
178
+ for (const item of value)
179
+ collectObjects(item, key, objects, 1);
180
+ }
181
+ else {
182
+ collectObjects(value, key, objects, 1);
183
+ }
184
+ }
185
+ const name = attributes.name ?? attributes.Name ?? fallbackName;
186
+ return { file, name, objects };
187
+ }
188
+ catch (error) {
189
+ return { file, name: fallbackName, objects: [], parseError: error instanceof Error ? error.message : String(error) };
190
+ }
191
+ }
192
+ /** List one directory through the provider, or Node when the provider cannot list. */
193
+ async function listDirectory(fs, path, signal) {
194
+ if (fs.listDir !== undefined)
195
+ return await fs.listDir(path, signal);
196
+ const entries = await readdir(path, { withFileTypes: true });
197
+ return entries.map(entry => ({ name: entry.name, type: entry.isDirectory() ? 'directory' : 'file' }));
198
+ }
199
+ /** Walk the export folder, calling `visit` for every regular file. */
200
+ async function walk(fs, root, relative, signal, visit, budget) {
201
+ signal?.throwIfAborted();
202
+ const path = relative === '' ? root : `${root.replace(/[\\/]$/u, '')}/${relative}`;
203
+ let entries;
204
+ try {
205
+ entries = await listDirectory(fs, path, signal);
206
+ }
207
+ catch (error) {
208
+ throw new EngineeringError('VIEW_UNREADABLE', `Cannot list ${path}: ${error instanceof Error ? error.message : String(error)}`, 'Pass an existing export folder the session can read.');
209
+ }
210
+ for (const entry of [...entries].sort((left, right) => left.name.localeCompare(right.name))) {
211
+ signal?.throwIfAborted();
212
+ const child = relative === '' ? entry.name : `${relative}/${entry.name}`;
213
+ if (entry.type === 'directory') {
214
+ await walk(fs, root, child, signal, visit, budget);
215
+ continue;
216
+ }
217
+ if (!/\.(xml|csv)$/iu.test(entry.name))
218
+ continue;
219
+ budget.count += 1;
220
+ if (budget.count > budget.max) {
221
+ throw new EngineeringError('VIEW_TOO_LARGE', `The export has more than ${budget.max} display and tag files.`, 'Point ftview_open at one application export folder, not a whole drive.');
222
+ }
223
+ await visit(child, `${root.replace(/[\\/]$/u, '')}/${child}`);
224
+ }
225
+ }
226
+ /**
227
+ * Scan one export folder.
228
+ *
229
+ * @param fs - Filesystem provider.
230
+ * @param root - Resolved export folder.
231
+ * @param signal - Cancellation.
232
+ * @param maxFiles - Largest number of display/tag files accepted.
233
+ * @returns The scanned application.
234
+ */
235
+ export async function scanApplication(fs, root, signal, maxFiles) {
236
+ const displays = [];
237
+ const tags = [];
238
+ const tagFiles = [];
239
+ const tagErrors = [];
240
+ const diagnostics = [];
241
+ const files = [];
242
+ await walk(fs, root, '', signal, async (file, absolute) => {
243
+ const bytes = await fs.readBytes(absolute, signal, 32 * 1024 * 1024);
244
+ const text = decode(bytes);
245
+ files.push({ file, bytes: bytes.length, sha256: createHash('sha256').update(bytes).digest('hex') });
246
+ if (/\.csv$/iu.test(file)) {
247
+ const csv = parseTagCsv(text);
248
+ if (csv.rows.length === 0 && csv.errors.length > 0) {
249
+ diagnostics.push(`${file}: ${csv.errors.join(' ')}`);
250
+ return;
251
+ }
252
+ tagFiles.push(file);
253
+ tags.push(...csv.rows);
254
+ for (const error of csv.errors)
255
+ tagErrors.push(`${file}: ${error}`);
256
+ return;
257
+ }
258
+ const display = parseDisplay(file, text);
259
+ if (display.parseError !== undefined) {
260
+ diagnostics.push(`${file}: ${display.parseError}`);
261
+ return;
262
+ }
263
+ displays.push(display);
264
+ }, { count: 0, max: maxFiles });
265
+ const name = root.replace(/[\\/]$/u, '').split(/[\\/]/u).pop() ?? root;
266
+ return { root, name, displays, tags, tagFiles, tagErrors, diagnostics, files };
267
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Open ME export folders for one chat.
3
+ *
4
+ * A folder is scanned once when opened; later calls read the same application
5
+ * model. Handles belong to the calling chat and are bounded, so an abandoned
6
+ * conversation does not retain export trees.
7
+ *
8
+ * @module dsh-engineering-ftview/session
9
+ */
10
+ import { type ExecContext, type FsService } from './core.ts';
11
+ import { type Application } from './model.ts';
12
+ /** One opened export folder. */
13
+ export interface OpenView {
14
+ readonly id: string;
15
+ readonly app: Application;
16
+ }
17
+ /** The scanned export store, keyed by chat. */
18
+ export declare class ViewSession {
19
+ readonly fs: FsService;
20
+ private readonly maxFiles;
21
+ private readonly maxViews;
22
+ private readonly chats;
23
+ private readonly detached;
24
+ /**
25
+ * @param fs - Filesystem provider.
26
+ * @param maxFiles - Most display and tag files one open may scan.
27
+ * @param maxViews - Most export folders one chat retains.
28
+ */
29
+ constructor(fs: FsService, maxFiles: number, maxViews?: number);
30
+ /**
31
+ * Resolve, check and scan one export folder for the calling chat.
32
+ *
33
+ * @param requestedRoot - Folder as the caller supplied it.
34
+ * @param exec - Calling context: cancellation, workspace and chat identity.
35
+ * @returns The retained view.
36
+ */
37
+ open(requestedRoot: string, exec?: ExecContext): Promise<OpenView>;
38
+ /**
39
+ * Resolve the view a call applies to.
40
+ *
41
+ * @param viewId - Explicit handle, or undefined to use the chat's only view.
42
+ * @param exec - Calling context.
43
+ * @returns The open view.
44
+ */
45
+ require(viewId: string | undefined, exec?: ExecContext): OpenView;
46
+ /** The view map for one calling chat. */
47
+ private viewsOf;
48
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Open ME export folders for one chat.
3
+ *
4
+ * A folder is scanned once when opened; later calls read the same application
5
+ * model. Handles belong to the calling chat and are bounded, so an abandoned
6
+ * conversation does not retain export trees.
7
+ *
8
+ * @module dsh-engineering-ftview/session
9
+ */
10
+ import { randomUUID } from 'node:crypto';
11
+ import { EngineeringError } from "./core.js";
12
+ import { scanApplication } from "./model.js";
13
+ /** The scanned export store, keyed by chat. */
14
+ export class ViewSession {
15
+ fs;
16
+ maxFiles;
17
+ maxViews;
18
+ chats = new WeakMap();
19
+ detached = new Map();
20
+ /**
21
+ * @param fs - Filesystem provider.
22
+ * @param maxFiles - Most display and tag files one open may scan.
23
+ * @param maxViews - Most export folders one chat retains.
24
+ */
25
+ constructor(fs, maxFiles, maxViews = 2) {
26
+ this.fs = fs;
27
+ this.maxFiles = maxFiles;
28
+ this.maxViews = maxViews;
29
+ }
30
+ /**
31
+ * Resolve, check and scan one export folder for the calling chat.
32
+ *
33
+ * @param requestedRoot - Folder as the caller supplied it.
34
+ * @param exec - Calling context: cancellation, workspace and chat identity.
35
+ * @returns The retained view.
36
+ */
37
+ async open(requestedRoot, exec) {
38
+ const root = await this.fs.resolve(requestedRoot, {
39
+ ...(exec?.cwd === undefined ? {} : { cwd: exec.cwd }),
40
+ ...(exec?.signal === undefined ? {} : { signal: exec.signal }),
41
+ });
42
+ const info = await this.fs.stat(root, exec?.signal);
43
+ if (info === undefined) {
44
+ throw new EngineeringError('VIEW_NOT_FOUND', `No export folder exists at ${requestedRoot}.`, 'Pass an absolute path or a path inside this workspace, for example "C:/Projects/Line/ME Export".');
45
+ }
46
+ if (info.type !== 'directory') {
47
+ throw new EngineeringError('INVALID_VIEW', `${requestedRoot} is not a folder.`, 'Pass the export folder that contains the display XML files and the tag CSV.');
48
+ }
49
+ const app = await scanApplication(this.fs, root, exec?.signal, this.maxFiles);
50
+ const view = { id: randomUUID(), app };
51
+ const views = this.viewsOf(exec);
52
+ views.set(view.id, view);
53
+ while (views.size > this.maxViews) {
54
+ const oldest = views.keys().next().value;
55
+ if (oldest !== undefined)
56
+ views.delete(oldest);
57
+ }
58
+ return view;
59
+ }
60
+ /**
61
+ * Resolve the view a call applies to.
62
+ *
63
+ * @param viewId - Explicit handle, or undefined to use the chat's only view.
64
+ * @param exec - Calling context.
65
+ * @returns The open view.
66
+ */
67
+ require(viewId, exec) {
68
+ const views = this.viewsOf(exec);
69
+ const ids = [...views.keys()];
70
+ if (viewId === undefined || viewId === '') {
71
+ const only = ids[0];
72
+ if (only === undefined) {
73
+ throw new EngineeringError('NO_VIEW', 'No ME export is open in this chat.', 'Call ftview_open with the export folder first.');
74
+ }
75
+ if (ids.length > 1) {
76
+ throw new EngineeringError('AMBIGUOUS_VIEW', `${ids.length} ME exports are open in this chat, so view_id is required.`, `Pass one of this chat's view ids: ${ids.join(', ')}.`, ids);
77
+ }
78
+ return views.get(only);
79
+ }
80
+ const view = views.get(viewId);
81
+ if (view === undefined) {
82
+ throw new EngineeringError('UNKNOWN_VIEW', `No ME export is open with view_id ${JSON.stringify(viewId)} in this chat.`, ids.length === 0 ? 'Call ftview_open with the export folder first.' : `Pass one of this chat's view ids: ${ids.join(', ')}.`, ids);
83
+ }
84
+ return view;
85
+ }
86
+ /** The view map for one calling chat. */
87
+ viewsOf(exec) {
88
+ const chat = exec?.chat;
89
+ if (chat === undefined)
90
+ return this.detached;
91
+ const existing = this.chats.get(chat);
92
+ if (existing !== undefined)
93
+ return existing;
94
+ const created = new Map();
95
+ this.chats.set(chat, created);
96
+ return created;
97
+ }
98
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The guidance half of the FTView port.
3
+ *
4
+ * Registers the `factorytalk-view` skill from SKILL.md. It is a separate loader
5
+ * entry, so a profile can run the tools without the guidance or the guidance
6
+ * without the tools.
7
+ *
8
+ * @module dsh-engineering-ftview/skill
9
+ */
10
+ /** Plugin name in a composition. */
11
+ export declare const name = "ftview-read-skill";
12
+ /** Services this plugin cannot register without. */
13
+ export declare const inject: readonly ["skills"];
14
+ /** The plugin context this module needs. */
15
+ export interface SkillContext {
16
+ get(name: string): unknown;
17
+ }
18
+ /** Registration metadata, kept here so a test can assert it without the file. */
19
+ export declare const SKILL: {
20
+ readonly name: "factorytalk-view";
21
+ readonly description: "Design and support FactoryTalk View ME and PanelView Plus applications from documented text exports: displays, HMI tags, structural checks and export comparison.";
22
+ readonly whenToUse: "Use when the user asks about FactoryTalk View ME, PanelView Plus, ME display exports, HMI tags, or comparing ME exports. Route FactoryTalk View SE and Optix to their own surfaces.";
23
+ };
24
+ /**
25
+ * Register the FactoryTalk View skill.
26
+ *
27
+ * @param ctx - Plugin context whose skill registry receives the registration.
28
+ */
29
+ export declare function apply(ctx: SkillContext): void;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The guidance half of the FTView port.
3
+ *
4
+ * Registers the `factorytalk-view` skill from SKILL.md. It is a separate loader
5
+ * entry, so a profile can run the tools without the guidance or the guidance
6
+ * without the tools.
7
+ *
8
+ * @module dsh-engineering-ftview/skill
9
+ */
10
+ import { readFileSync } from 'node:fs';
11
+ import { fileURLToPath } from 'node:url';
12
+ /** Plugin name in a composition. */
13
+ export const name = 'ftview-read-skill';
14
+ /** Services this plugin cannot register without. */
15
+ export const inject = ['skills'];
16
+ /** Registration metadata, kept here so a test can assert it without the file. */
17
+ export const SKILL = {
18
+ name: 'factorytalk-view',
19
+ description: 'Design and support FactoryTalk View ME and PanelView Plus applications from documented text exports: displays, HMI tags, structural checks and export comparison.',
20
+ whenToUse: 'Use when the user asks about FactoryTalk View ME, PanelView Plus, ME display exports, HMI tags, or comparing ME exports. Route FactoryTalk View SE and Optix to their own surfaces.',
21
+ };
22
+ /**
23
+ * Register the FactoryTalk View skill.
24
+ *
25
+ * @param ctx - Plugin context whose skill registry receives the registration.
26
+ */
27
+ export function apply(ctx) {
28
+ const skills = ctx.get('skills');
29
+ if (skills === undefined || typeof skills.register !== 'function') {
30
+ throw new Error('ftview-read-skill: the skills service is unavailable, so the factorytalk-view guidance cannot register');
31
+ }
32
+ const skillPath = fileURLToPath(new URL('../SKILL.md', import.meta.url));
33
+ const raw = readFileSync(skillPath, 'utf8');
34
+ const content = raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/u, '');
35
+ skills.register({
36
+ ...SKILL,
37
+ content,
38
+ path: skillPath,
39
+ source: 'custom',
40
+ provider: '@aryacode/dsh-engineering-ftview',
41
+ invocation: { modelInvocable: true, userInvocable: true },
42
+ });
43
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Bounded discovery of FactoryTalk View Studio ME tooling.
3
+ *
4
+ * Reads directories through the filesystem provider, never executes anything.
5
+ * A missing install is reported as such; absence of a utility is not an error,
6
+ * because the manual Studio workflow does not need it.
7
+ *
8
+ * @module dsh-engineering-ftview/studio/discover
9
+ */
10
+ import type { FsService } from '../core.ts';
11
+ /** One executable the discovery found. */
12
+ export interface DiscoveredUtility {
13
+ readonly path: string;
14
+ readonly bytes: number;
15
+ readonly sha256: string;
16
+ }
17
+ /** What the discovery found. */
18
+ export interface StudioDiscovery {
19
+ readonly searched: readonly string[];
20
+ readonly products: readonly string[];
21
+ readonly schemas: readonly string[];
22
+ readonly utilities: readonly DiscoveredUtility[];
23
+ readonly help: readonly string[];
24
+ readonly truncated: boolean;
25
+ }
26
+ /** Known install roots for FactoryTalk View on Windows. */
27
+ export declare const DEFAULT_SEARCH_ROOTS: readonly string[];
28
+ /**
29
+ * Discover View Studio tooling under the configured roots.
30
+ *
31
+ * @param fs - Filesystem provider.
32
+ * @param roots - Absolute roots to search.
33
+ * @param signal - Cancellation.
34
+ * @returns What was found, plus the roots that were searched.
35
+ */
36
+ export declare function discoverStudio(fs: FsService, roots: readonly string[], signal: AbortSignal | undefined): Promise<StudioDiscovery>;