webmcp-cli 1.2.2 → 1.2.3

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 (330) hide show
  1. package/dist/analysis/form-to-tool-mapper.d.ts +61 -0
  2. package/dist/analysis/form-to-tool-mapper.js +360 -0
  3. package/dist/analysis/form-to-tool-mapper.js.map +1 -0
  4. package/dist/analysis/index.d.ts +84 -0
  5. package/dist/analysis/index.js +81 -0
  6. package/dist/analysis/index.js.map +1 -0
  7. package/dist/analysis/missing-tool-analyzer.d.ts +35 -0
  8. package/dist/analysis/missing-tool-analyzer.js +617 -0
  9. package/dist/analysis/missing-tool-analyzer.js.map +1 -0
  10. package/dist/audit/run-multi-page-audit.d.ts +34 -0
  11. package/dist/audit/run-multi-page-audit.js +233 -0
  12. package/dist/audit/run-multi-page-audit.js.map +1 -0
  13. package/dist/cli/commands/potential.d.ts +8 -0
  14. package/dist/cli/commands/potential.js +323 -0
  15. package/dist/cli/commands/potential.js.map +1 -0
  16. package/dist/cli/commands/report.d.ts +12 -0
  17. package/dist/cli/commands/report.js +89 -0
  18. package/dist/cli/commands/report.js.map +1 -0
  19. package/dist/cli/index.js +35 -0
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/config/defaults.d.ts +36 -0
  22. package/dist/config/defaults.js +33 -0
  23. package/dist/config/defaults.js.map +1 -0
  24. package/dist/config/index.d.ts +7 -0
  25. package/dist/config/index.js +7 -0
  26. package/dist/config/index.js.map +1 -0
  27. package/dist/config/loader.d.ts +22 -0
  28. package/dist/config/loader.js +91 -0
  29. package/dist/config/loader.js.map +1 -0
  30. package/dist/config/schema.d.ts +280 -0
  31. package/dist/config/schema.js +42 -0
  32. package/dist/config/schema.js.map +1 -0
  33. package/dist/core/types/audit.d.ts +1 -1
  34. package/dist/core/types/index.d.ts +1 -0
  35. package/dist/core/types/index.js +1 -0
  36. package/dist/core/types/index.js.map +1 -1
  37. package/dist/core/types/recon.d.ts +265 -0
  38. package/dist/core/types/recon.js +5 -0
  39. package/dist/core/types/recon.js.map +1 -0
  40. package/dist/core/types/rule.d.ts +1 -1
  41. package/dist/core/types/rule.js +7 -5
  42. package/dist/core/types/rule.js.map +1 -1
  43. package/dist/crawler/depth-crawler.d.ts +29 -0
  44. package/dist/crawler/depth-crawler.js +212 -0
  45. package/dist/crawler/depth-crawler.js.map +1 -0
  46. package/dist/crawler/index.d.ts +2 -0
  47. package/dist/crawler/index.js +3 -0
  48. package/dist/crawler/index.js.map +1 -0
  49. package/dist/crawler/link-extractor.d.ts +1 -0
  50. package/dist/crawler/link-extractor.js +49 -0
  51. package/dist/crawler/link-extractor.js.map +1 -0
  52. package/dist/generators/index.d.ts +10 -0
  53. package/dist/generators/index.js +8 -0
  54. package/dist/generators/index.js.map +1 -0
  55. package/dist/generators/report-html.d.ts +12 -0
  56. package/dist/generators/report-html.js +470 -0
  57. package/dist/generators/report-html.js.map +1 -0
  58. package/dist/generators/report-json.d.ts +95 -0
  59. package/dist/generators/report-json.js +144 -0
  60. package/dist/generators/report-json.js.map +1 -0
  61. package/dist/generators/report-manager.d.ts +31 -0
  62. package/dist/generators/report-manager.js +208 -0
  63. package/dist/generators/report-manager.js.map +1 -0
  64. package/dist/generators/tool-code-generator.d.ts +31 -0
  65. package/dist/generators/tool-code-generator.js +201 -0
  66. package/dist/generators/tool-code-generator.js.map +1 -0
  67. package/dist/potential/ai-recommender.d.ts +33 -0
  68. package/dist/potential/ai-recommender.js +414 -0
  69. package/dist/potential/ai-recommender.js.map +1 -0
  70. package/dist/potential/analyzer.d.ts +32 -0
  71. package/dist/potential/analyzer.js +383 -0
  72. package/dist/potential/analyzer.js.map +1 -0
  73. package/dist/potential/index.d.ts +3 -0
  74. package/dist/potential/index.js +4 -0
  75. package/dist/potential/index.js.map +1 -0
  76. package/dist/potential/prompts.d.ts +20 -0
  77. package/dist/potential/prompts.js +42 -0
  78. package/dist/potential/prompts.js.map +1 -0
  79. package/dist/potential/types.d.ts +40 -0
  80. package/dist/potential/types.js +2 -0
  81. package/dist/potential/types.js.map +1 -0
  82. package/dist/recon/index.d.ts +20 -0
  83. package/dist/recon/index.js +143 -0
  84. package/dist/recon/index.js.map +1 -0
  85. package/dist/recon/manifest.d.ts +16 -0
  86. package/dist/recon/manifest.js +108 -0
  87. package/dist/recon/manifest.js.map +1 -0
  88. package/dist/recon/meta-extractor.d.ts +11 -0
  89. package/dist/recon/meta-extractor.js +276 -0
  90. package/dist/recon/meta-extractor.js.map +1 -0
  91. package/dist/recon/robots.d.ts +16 -0
  92. package/dist/recon/robots.js +158 -0
  93. package/dist/recon/robots.js.map +1 -0
  94. package/dist/recon/route-discovery.d.ts +25 -0
  95. package/dist/recon/route-discovery.js +303 -0
  96. package/dist/recon/route-discovery.js.map +1 -0
  97. package/dist/recon/sitemap.d.ts +12 -0
  98. package/dist/recon/sitemap.js +177 -0
  99. package/dist/recon/sitemap.js.map +1 -0
  100. package/dist/rules/accessibility/AXE-001.d.ts +9 -0
  101. package/dist/rules/accessibility/AXE-001.js +109 -0
  102. package/dist/rules/accessibility/AXE-001.js.map +1 -0
  103. package/dist/rules/accessibility/AXE-002.d.ts +8 -0
  104. package/dist/rules/accessibility/AXE-002.js +85 -0
  105. package/dist/rules/accessibility/AXE-002.js.map +1 -0
  106. package/dist/rules/accessibility/AXE-003.d.ts +8 -0
  107. package/dist/rules/accessibility/AXE-003.js +94 -0
  108. package/dist/rules/accessibility/AXE-003.js.map +1 -0
  109. package/dist/rules/accessibility/AXE-004.d.ts +8 -0
  110. package/dist/rules/accessibility/AXE-004.js +101 -0
  111. package/dist/rules/accessibility/AXE-004.js.map +1 -0
  112. package/dist/rules/accessibility/AXE-005.d.ts +9 -0
  113. package/dist/rules/accessibility/AXE-005.js +89 -0
  114. package/dist/rules/accessibility/AXE-005.js.map +1 -0
  115. package/dist/rules/best-practices/BP-004.d.ts +9 -0
  116. package/dist/rules/best-practices/BP-004.js +96 -0
  117. package/dist/rules/best-practices/BP-004.js.map +1 -0
  118. package/dist/rules/best-practices/BP-005.d.ts +8 -0
  119. package/dist/rules/best-practices/BP-005.js +94 -0
  120. package/dist/rules/best-practices/BP-005.js.map +1 -0
  121. package/dist/rules/best-practices/BP-006.d.ts +8 -0
  122. package/dist/rules/best-practices/BP-006.js +80 -0
  123. package/dist/rules/best-practices/BP-006.js.map +1 -0
  124. package/dist/rules/best-practices/BP-007.d.ts +8 -0
  125. package/dist/rules/best-practices/BP-007.js +92 -0
  126. package/dist/rules/best-practices/BP-007.js.map +1 -0
  127. package/dist/rules/best-practices/BP-008.d.ts +12 -0
  128. package/dist/rules/best-practices/BP-008.js +86 -0
  129. package/dist/rules/best-practices/BP-008.js.map +1 -0
  130. package/dist/rules/best-practices/BP-009.d.ts +9 -0
  131. package/dist/rules/best-practices/BP-009.js +77 -0
  132. package/dist/rules/best-practices/BP-009.js.map +1 -0
  133. package/dist/rules/best-practices/BP-010.d.ts +8 -0
  134. package/dist/rules/best-practices/BP-010.js +85 -0
  135. package/dist/rules/best-practices/BP-010.js.map +1 -0
  136. package/dist/rules/coverage/COV-002.d.ts +8 -0
  137. package/dist/rules/coverage/COV-002.js +68 -0
  138. package/dist/rules/coverage/COV-002.js.map +1 -0
  139. package/dist/rules/coverage/COV-003.d.ts +8 -0
  140. package/dist/rules/coverage/COV-003.js +68 -0
  141. package/dist/rules/coverage/COV-003.js.map +1 -0
  142. package/dist/rules/coverage/COV-004.d.ts +8 -0
  143. package/dist/rules/coverage/COV-004.js +89 -0
  144. package/dist/rules/coverage/COV-004.js.map +1 -0
  145. package/dist/rules/coverage/COV-005.d.ts +8 -0
  146. package/dist/rules/coverage/COV-005.js +67 -0
  147. package/dist/rules/coverage/COV-005.js.map +1 -0
  148. package/dist/rules/coverage/COV-006.d.ts +9 -0
  149. package/dist/rules/coverage/COV-006.js +76 -0
  150. package/dist/rules/coverage/COV-006.js.map +1 -0
  151. package/dist/rules/coverage/COV-007.d.ts +8 -0
  152. package/dist/rules/coverage/COV-007.js +67 -0
  153. package/dist/rules/coverage/COV-007.js.map +1 -0
  154. package/dist/rules/coverage/COV-008.d.ts +9 -0
  155. package/dist/rules/coverage/COV-008.js +87 -0
  156. package/dist/rules/coverage/COV-008.js.map +1 -0
  157. package/dist/rules/coverage/COV-009.d.ts +8 -0
  158. package/dist/rules/coverage/COV-009.js +73 -0
  159. package/dist/rules/coverage/COV-009.js.map +1 -0
  160. package/dist/rules/coverage/COV-010.d.ts +9 -0
  161. package/dist/rules/coverage/COV-010.js +82 -0
  162. package/dist/rules/coverage/COV-010.js.map +1 -0
  163. package/dist/rules/description/DESC-001.d.ts +9 -0
  164. package/dist/rules/description/DESC-001.js +88 -0
  165. package/dist/rules/description/DESC-001.js.map +1 -0
  166. package/dist/rules/description/DESC-002.d.ts +10 -0
  167. package/dist/rules/description/DESC-002.js +99 -0
  168. package/dist/rules/description/DESC-002.js.map +1 -0
  169. package/dist/rules/description/DESC-006.d.ts +9 -0
  170. package/dist/rules/description/DESC-006.js +78 -0
  171. package/dist/rules/description/DESC-006.js.map +1 -0
  172. package/dist/rules/description/DESC-007.d.ts +9 -0
  173. package/dist/rules/description/DESC-007.js +70 -0
  174. package/dist/rules/description/DESC-007.js.map +1 -0
  175. package/dist/rules/description/DESC-008.d.ts +9 -0
  176. package/dist/rules/description/DESC-008.js +70 -0
  177. package/dist/rules/description/DESC-008.js.map +1 -0
  178. package/dist/rules/description/DESC-009.d.ts +8 -0
  179. package/dist/rules/description/DESC-009.js +55 -0
  180. package/dist/rules/description/DESC-009.js.map +1 -0
  181. package/dist/rules/description/DESC-010.d.ts +9 -0
  182. package/dist/rules/description/DESC-010.js +92 -0
  183. package/dist/rules/description/DESC-010.js.map +1 -0
  184. package/dist/rules/description/DESC-011.d.ts +9 -0
  185. package/dist/rules/description/DESC-011.js +81 -0
  186. package/dist/rules/description/DESC-011.js.map +1 -0
  187. package/dist/rules/description/DESC-012.d.ts +9 -0
  188. package/dist/rules/description/DESC-012.js +98 -0
  189. package/dist/rules/description/DESC-012.js.map +1 -0
  190. package/dist/rules/implementation/IMP-002.d.ts +9 -0
  191. package/dist/rules/implementation/IMP-002.js +59 -0
  192. package/dist/rules/implementation/IMP-002.js.map +1 -0
  193. package/dist/rules/implementation/IMP-006.d.ts +9 -0
  194. package/dist/rules/implementation/IMP-006.js +48 -0
  195. package/dist/rules/implementation/IMP-006.js.map +1 -0
  196. package/dist/rules/implementation/IMP-008.d.ts +9 -0
  197. package/dist/rules/implementation/IMP-008.js +46 -0
  198. package/dist/rules/implementation/IMP-008.js.map +1 -0
  199. package/dist/rules/implementation/IMP-009.d.ts +9 -0
  200. package/dist/rules/implementation/IMP-009.js +48 -0
  201. package/dist/rules/implementation/IMP-009.js.map +1 -0
  202. package/dist/rules/implementation/IMP-010.d.ts +9 -0
  203. package/dist/rules/implementation/IMP-010.js +66 -0
  204. package/dist/rules/implementation/IMP-010.js.map +1 -0
  205. package/dist/rules/implementation/IMP-011.d.ts +9 -0
  206. package/dist/rules/implementation/IMP-011.js +82 -0
  207. package/dist/rules/implementation/IMP-011.js.map +1 -0
  208. package/dist/rules/implementation/IMP-012.d.ts +9 -0
  209. package/dist/rules/implementation/IMP-012.js +88 -0
  210. package/dist/rules/implementation/IMP-012.js.map +1 -0
  211. package/dist/rules/implementation/IMP-014.d.ts +9 -0
  212. package/dist/rules/implementation/IMP-014.js +58 -0
  213. package/dist/rules/implementation/IMP-014.js.map +1 -0
  214. package/dist/rules/implementation/IMP-015.d.ts +9 -0
  215. package/dist/rules/implementation/IMP-015.js +64 -0
  216. package/dist/rules/implementation/IMP-015.js.map +1 -0
  217. package/dist/rules/implementation/IMP-016.d.ts +9 -0
  218. package/dist/rules/implementation/IMP-016.js +52 -0
  219. package/dist/rules/implementation/IMP-016.js.map +1 -0
  220. package/dist/rules/implementation/IMP-017.d.ts +8 -0
  221. package/dist/rules/implementation/IMP-017.js +51 -0
  222. package/dist/rules/implementation/IMP-017.js.map +1 -0
  223. package/dist/rules/implementation/IMP-018.d.ts +8 -0
  224. package/dist/rules/implementation/IMP-018.js +52 -0
  225. package/dist/rules/implementation/IMP-018.js.map +1 -0
  226. package/dist/rules/implementation/IMP-019.d.ts +8 -0
  227. package/dist/rules/implementation/IMP-019.js +53 -0
  228. package/dist/rules/implementation/IMP-019.js.map +1 -0
  229. package/dist/rules/implementation/IMP-020.d.ts +9 -0
  230. package/dist/rules/implementation/IMP-020.js +62 -0
  231. package/dist/rules/implementation/IMP-020.js.map +1 -0
  232. package/dist/rules/implementation/IMP-021.d.ts +8 -0
  233. package/dist/rules/implementation/IMP-021.js +64 -0
  234. package/dist/rules/implementation/IMP-021.js.map +1 -0
  235. package/dist/rules/implementation/IMP-022.d.ts +8 -0
  236. package/dist/rules/implementation/IMP-022.js +70 -0
  237. package/dist/rules/implementation/IMP-022.js.map +1 -0
  238. package/dist/rules/index.d.ts +73 -6
  239. package/dist/rules/index.js +141 -6
  240. package/dist/rules/index.js.map +1 -1
  241. package/dist/rules/schema/SCHEMA-004.d.ts +9 -0
  242. package/dist/rules/schema/SCHEMA-004.js +57 -0
  243. package/dist/rules/schema/SCHEMA-004.js.map +1 -0
  244. package/dist/rules/schema/SCHEMA-005.d.ts +9 -0
  245. package/dist/rules/schema/SCHEMA-005.js +61 -0
  246. package/dist/rules/schema/SCHEMA-005.js.map +1 -0
  247. package/dist/rules/schema/SCHEMA-006.d.ts +10 -0
  248. package/dist/rules/schema/SCHEMA-006.js +85 -0
  249. package/dist/rules/schema/SCHEMA-006.js.map +1 -0
  250. package/dist/rules/schema/SCHEMA-007.d.ts +9 -0
  251. package/dist/rules/schema/SCHEMA-007.js +73 -0
  252. package/dist/rules/schema/SCHEMA-007.js.map +1 -0
  253. package/dist/rules/schema/SCHEMA-008.d.ts +9 -0
  254. package/dist/rules/schema/SCHEMA-008.js +70 -0
  255. package/dist/rules/schema/SCHEMA-008.js.map +1 -0
  256. package/dist/rules/schema/SCHEMA-009.d.ts +10 -0
  257. package/dist/rules/schema/SCHEMA-009.js +80 -0
  258. package/dist/rules/schema/SCHEMA-009.js.map +1 -0
  259. package/dist/rules/schema/SCHEMA-010.d.ts +9 -0
  260. package/dist/rules/schema/SCHEMA-010.js +96 -0
  261. package/dist/rules/schema/SCHEMA-010.js.map +1 -0
  262. package/dist/rules/schema/SCHEMA-012.d.ts +9 -0
  263. package/dist/rules/schema/SCHEMA-012.js +65 -0
  264. package/dist/rules/schema/SCHEMA-012.js.map +1 -0
  265. package/dist/rules/security/SEC-002.d.ts +8 -0
  266. package/dist/rules/security/SEC-002.js +81 -0
  267. package/dist/rules/security/SEC-002.js.map +1 -0
  268. package/dist/rules/security/SEC-003.d.ts +8 -0
  269. package/dist/rules/security/SEC-003.js +85 -0
  270. package/dist/rules/security/SEC-003.js.map +1 -0
  271. package/dist/rules/security/SEC-004.d.ts +9 -0
  272. package/dist/rules/security/SEC-004.js +87 -0
  273. package/dist/rules/security/SEC-004.js.map +1 -0
  274. package/dist/rules/security/SEC-005.d.ts +8 -0
  275. package/dist/rules/security/SEC-005.js +87 -0
  276. package/dist/rules/security/SEC-005.js.map +1 -0
  277. package/dist/rules/security/SEC-006.d.ts +10 -0
  278. package/dist/rules/security/SEC-006.js +108 -0
  279. package/dist/rules/security/SEC-006.js.map +1 -0
  280. package/dist/rules/security/SEC-007.d.ts +9 -0
  281. package/dist/rules/security/SEC-007.js +108 -0
  282. package/dist/rules/security/SEC-007.js.map +1 -0
  283. package/dist/rules/security/SEC-008.d.ts +8 -0
  284. package/dist/rules/security/SEC-008.js +109 -0
  285. package/dist/rules/security/SEC-008.js.map +1 -0
  286. package/dist/rules/security/SEC-009.d.ts +9 -0
  287. package/dist/rules/security/SEC-009.js +93 -0
  288. package/dist/rules/security/SEC-009.js.map +1 -0
  289. package/dist/rules/security/SEC-010.d.ts +8 -0
  290. package/dist/rules/security/SEC-010.js +78 -0
  291. package/dist/rules/security/SEC-010.js.map +1 -0
  292. package/dist/rules/security/SEC-011.d.ts +8 -0
  293. package/dist/rules/security/SEC-011.js +93 -0
  294. package/dist/rules/security/SEC-011.js.map +1 -0
  295. package/dist/rules/security/SEC-012.d.ts +8 -0
  296. package/dist/rules/security/SEC-012.js +79 -0
  297. package/dist/rules/security/SEC-012.js.map +1 -0
  298. package/dist/rules/security/SEC-013.d.ts +9 -0
  299. package/dist/rules/security/SEC-013.js +107 -0
  300. package/dist/rules/security/SEC-013.js.map +1 -0
  301. package/dist/scoring/calculator.js +1 -0
  302. package/dist/scoring/calculator.js.map +1 -1
  303. package/dist/ui/ink/components/AIRecommendationCard.d.ts +11 -0
  304. package/dist/ui/ink/components/AIRecommendationCard.js +23 -0
  305. package/dist/ui/ink/components/AIRecommendationCard.js.map +1 -0
  306. package/dist/ui/ink/components/OpportunityList.d.ts +10 -0
  307. package/dist/ui/ink/components/OpportunityList.js +48 -0
  308. package/dist/ui/ink/components/OpportunityList.js.map +1 -0
  309. package/dist/ui/ink/components/PotentialPageCard.d.ts +13 -0
  310. package/dist/ui/ink/components/PotentialPageCard.js +43 -0
  311. package/dist/ui/ink/components/PotentialPageCard.js.map +1 -0
  312. package/dist/ui/ink/components/PotentialProgress.d.ts +16 -0
  313. package/dist/ui/ink/components/PotentialProgress.js +44 -0
  314. package/dist/ui/ink/components/PotentialProgress.js.map +1 -0
  315. package/dist/ui/ink/components/PotentialSummary.d.ts +10 -0
  316. package/dist/ui/ink/components/PotentialSummary.js +86 -0
  317. package/dist/ui/ink/components/PotentialSummary.js.map +1 -0
  318. package/dist/ui/ink/components/SuggestionCard.d.ts +34 -0
  319. package/dist/ui/ink/components/SuggestionCard.js +36 -0
  320. package/dist/ui/ink/components/SuggestionCard.js.map +1 -0
  321. package/dist/ui/ink/components/views/MultiPageCrawlView.d.ts +21 -0
  322. package/dist/ui/ink/components/views/MultiPageCrawlView.js +55 -0
  323. package/dist/ui/ink/components/views/MultiPageCrawlView.js.map +1 -0
  324. package/dist/ui/ink/components/views/PotentialView.d.ts +18 -0
  325. package/dist/ui/ink/components/views/PotentialView.js +74 -0
  326. package/dist/ui/ink/components/views/PotentialView.js.map +1 -0
  327. package/dist/ui/ink/components/views/ReconView.d.ts +22 -0
  328. package/dist/ui/ink/components/views/ReconView.js +30 -0
  329. package/dist/ui/ink/components/views/ReconView.js.map +1 -0
  330. package/package.json +2 -1
@@ -0,0 +1,9 @@
1
+ /**
2
+ * AXE-005: Focus Management
3
+ *
4
+ * Checks that tool activation properly manages keyboard focus.
5
+ * Execute functions that modify the DOM should move focus to
6
+ * the changed element or a relevant target.
7
+ */
8
+ import type { Rule } from '../../core/types/rule.js';
9
+ export declare const AXE_005: Rule;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * AXE-005: Focus Management
3
+ *
4
+ * Checks that tool activation properly manages keyboard focus.
5
+ * Execute functions that modify the DOM should move focus to
6
+ * the changed element or a relevant target.
7
+ */
8
+ import { createRuleResult } from '../runner.js';
9
+ const FOCUS_PATTERNS = [
10
+ /\.focus\s*\(\s*\)/,
11
+ /\.scrollIntoView\s*\(/,
12
+ /aria-live/,
13
+ /role\s*=\s*["']alert["']/,
14
+ /\.setAttribute\s*\(\s*['"]tabindex["']/,
15
+ /tabIndex/,
16
+ ];
17
+ const DOM_MUTATION_PATTERNS = [
18
+ /\.innerHTML\s*=/,
19
+ /\.appendChild\s*\(/,
20
+ /\.insertBefore\s*\(/,
21
+ /\.replaceChild\s*\(/,
22
+ /\.removeChild\s*\(/,
23
+ /\.replaceWith\s*\(/,
24
+ /\.insertAdjacentHTML\s*\(/,
25
+ /\.insertAdjacentElement\s*\(/,
26
+ /\.after\s*\(/,
27
+ /\.before\s*\(/,
28
+ /\.prepend\s*\(/,
29
+ /\.append\s*\(/,
30
+ ];
31
+ export const AXE_005 = {
32
+ id: 'AXE-005',
33
+ category: 'accessibility',
34
+ name: 'Focus Management',
35
+ description: 'Tool activation should properly manage keyboard focus after DOM changes',
36
+ severity: 'info',
37
+ maxScore: 5,
38
+ async check(context) {
39
+ const tools = context.tools;
40
+ if (tools.length === 0) {
41
+ return createRuleResult('AXE-005', 5, {
42
+ passed: true,
43
+ score: 5,
44
+ message: 'No tools detected (rule not applicable)',
45
+ });
46
+ }
47
+ const toolsWithExecute = tools.filter((t) => t.executeSource && t.executeSource.length > 0);
48
+ if (toolsWithExecute.length === 0) {
49
+ return createRuleResult('AXE-005', 5, {
50
+ passed: true,
51
+ score: 5,
52
+ message: 'No execute functions available for analysis',
53
+ });
54
+ }
55
+ const violations = [];
56
+ for (const tool of toolsWithExecute) {
57
+ const src = tool.executeSource ?? '';
58
+ const mutatesDOM = DOM_MUTATION_PATTERNS.some((p) => p.test(src));
59
+ if (!mutatesDOM)
60
+ continue;
61
+ const managesFocus = FOCUS_PATTERNS.some((p) => p.test(src));
62
+ if (!managesFocus) {
63
+ violations.push(tool.name);
64
+ }
65
+ }
66
+ if (violations.length === 0) {
67
+ return createRuleResult('AXE-005', 5, {
68
+ passed: true,
69
+ score: 5,
70
+ message: 'Tools that modify DOM properly manage focus',
71
+ });
72
+ }
73
+ const penalty = Math.min(violations.length, 5);
74
+ const score = Math.max(0, 5 - penalty);
75
+ return createRuleResult('AXE-005', 5, {
76
+ passed: false,
77
+ score,
78
+ message: `${violations.length} tool(s) modify DOM without managing focus`,
79
+ details: violations.map((name) => `Tool "${name}" modifies the DOM but doesn't manage keyboard focus`),
80
+ suggestions: [
81
+ 'Call .focus() on the new or changed element after DOM updates',
82
+ 'Use aria-live regions to announce changes to screen readers',
83
+ 'Set tabindex="-1" on non-interactive elements before calling .focus()',
84
+ ],
85
+ affectedTools: violations,
86
+ });
87
+ },
88
+ };
89
+ //# sourceMappingURL=AXE-005.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AXE-005.js","sourceRoot":"","sources":["../../../src/rules/accessibility/AXE-005.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,cAAc,GAAG;IACrB,mBAAmB;IACnB,uBAAuB;IACvB,WAAW;IACX,0BAA0B;IAC1B,wCAAwC;IACxC,UAAU;CACX,CAAC;AAEF,MAAM,qBAAqB,GAAG;IAC5B,iBAAiB;IACjB,oBAAoB;IACpB,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;IACpB,oBAAoB;IACpB,2BAA2B;IAC3B,8BAA8B;IAC9B,cAAc;IACd,eAAe;IACf,gBAAgB;IAChB,eAAe;CAChB,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAS;IAC3B,EAAE,EAAE,SAAS;IACb,QAAQ,EAAE,eAAe;IACzB,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,yEAAyE;IAC3E,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,CAAC;IAEX,KAAK,CAAC,KAAK,CAAC,OAAoB;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAE5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,yCAAyC;aACnD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,CACnC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CACrD,CAAC;QAEF,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClC,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,6CAA6C;aACvD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAAa,EAAE,CAAC;QAEhC,KAAK,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;YAErC,MAAM,UAAU,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAClE,IAAI,CAAC,UAAU;gBAAE,SAAS;YAE1B,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7D,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;gBACpC,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,6CAA6C;aACvD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;QAEvC,OAAO,gBAAgB,CAAC,SAAS,EAAE,CAAC,EAAE;YACpC,MAAM,EAAE,KAAK;YACb,KAAK;YACL,OAAO,EAAE,GAAG,UAAU,CAAC,MAAM,4CAA4C;YACzE,OAAO,EAAE,UAAU,CAAC,GAAG,CACrB,CAAC,IAAI,EAAE,EAAE,CACP,SAAS,IAAI,sDAAsD,CACtE;YACD,WAAW,EAAE;gBACX,+DAA+D;gBAC/D,6DAA6D;gBAC7D,uEAAuE;aACxE;YACD,aAAa,EAAE,UAAU;SAC1B,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * BP-004: Error Recovery Info
3
+ *
4
+ * Checks that error responses from execute functions include
5
+ * enough information for an agent to understand what went wrong
6
+ * and potentially retry the operation.
7
+ */
8
+ import type { Rule } from '../../core/types/rule.js';
9
+ export declare const BP_004: Rule;
@@ -0,0 +1,96 @@
1
+ /**
2
+ * BP-004: Error Recovery Info
3
+ *
4
+ * Checks that error responses from execute functions include
5
+ * enough information for an agent to understand what went wrong
6
+ * and potentially retry the operation.
7
+ */
8
+ import { createRuleResult } from '../runner.js';
9
+ const ERROR_HANDLING_PATTERNS = [
10
+ /catch\s*\(/,
11
+ /\.catch\s*\(/,
12
+ /try\s*\{/,
13
+ ];
14
+ const GOOD_ERROR_PATTERNS = [
15
+ /message\s*:/,
16
+ /error\s*:/,
17
+ /reason\s*:/,
18
+ /code\s*:/,
19
+ /statusCode\s*:/,
20
+ /retryable\s*:/,
21
+ /retry/i,
22
+ /suggestion\s*:/,
23
+ /hint\s*:/,
24
+ ];
25
+ const BARE_THROW_PATTERN = /throw\s+(?:new\s+Error\s*\(\s*\)|['"`]['"`]|e\b|err\b)/;
26
+ export const BP_004 = {
27
+ id: 'BP-004',
28
+ category: 'best-practices',
29
+ name: 'Error Recovery Info',
30
+ description: 'Error responses should include enough information for agents to retry',
31
+ severity: 'info',
32
+ maxScore: 5,
33
+ async check(context) {
34
+ const tools = context.tools;
35
+ if (tools.length === 0) {
36
+ return createRuleResult('BP-004', 5, {
37
+ passed: true,
38
+ score: 5,
39
+ message: 'No tools detected (rule not applicable)',
40
+ });
41
+ }
42
+ const toolsWithExecute = tools.filter((t) => t.executeSource && t.executeSource.length > 0);
43
+ if (toolsWithExecute.length === 0) {
44
+ return createRuleResult('BP-004', 5, {
45
+ passed: true,
46
+ score: 5,
47
+ message: 'No execute functions available for analysis',
48
+ });
49
+ }
50
+ const violations = [];
51
+ const noErrorHandling = [];
52
+ for (const tool of toolsWithExecute) {
53
+ const src = tool.executeSource ?? '';
54
+ const hasErrorHandling = ERROR_HANDLING_PATTERNS.some((p) => p.test(src));
55
+ if (!hasErrorHandling) {
56
+ noErrorHandling.push(tool.name);
57
+ continue;
58
+ }
59
+ const hasGoodErrorInfo = GOOD_ERROR_PATTERNS.some((p) => p.test(src));
60
+ const hasBareThrow = BARE_THROW_PATTERN.test(src);
61
+ if (hasBareThrow && !hasGoodErrorInfo) {
62
+ violations.push(tool.name);
63
+ }
64
+ }
65
+ const allIssues = [...violations, ...noErrorHandling];
66
+ if (allIssues.length === 0) {
67
+ return createRuleResult('BP-004', 5, {
68
+ passed: true,
69
+ score: 5,
70
+ message: 'Execute functions provide good error recovery information',
71
+ });
72
+ }
73
+ const penalty = Math.min(allIssues.length, 5);
74
+ const score = Math.max(0, 5 - penalty);
75
+ const details = [];
76
+ for (const name of noErrorHandling) {
77
+ details.push(`Tool "${name}" has no error handling (try/catch)`);
78
+ }
79
+ for (const name of violations) {
80
+ details.push(`Tool "${name}" has error handling but throws bare errors without context`);
81
+ }
82
+ return createRuleResult('BP-004', 5, {
83
+ passed: false,
84
+ score,
85
+ message: `${allIssues.length} tool(s) have poor error recovery information`,
86
+ details,
87
+ suggestions: [
88
+ 'Include descriptive error messages with context about what failed',
89
+ 'Add a "retryable" flag to indicate if the agent can retry the operation',
90
+ 'Return structured error objects: { error: true, message: "...", code: "..." }',
91
+ ],
92
+ affectedTools: allIssues,
93
+ });
94
+ },
95
+ };
96
+ //# sourceMappingURL=BP-004.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BP-004.js","sourceRoot":"","sources":["../../../src/rules/best-practices/BP-004.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * BP-005: UI Sync After Execution
3
+ *
4
+ * Checks that execute functions update the DOM/UI after state changes,
5
+ * ensuring the page reflects the result of tool execution.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const BP_005: Rule;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * BP-005: UI Sync After Execution
3
+ *
4
+ * Checks that execute functions update the DOM/UI after state changes,
5
+ * ensuring the page reflects the result of tool execution.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ const STATE_CHANGE_PATTERNS = [
9
+ /fetch\s*\(.*method\s*:\s*['"](?:POST|PUT|DELETE|PATCH)/i,
10
+ /localStorage\.setItem/,
11
+ /sessionStorage\.setItem/,
12
+ /\.save\s*\(/,
13
+ /\.update\s*\(/,
14
+ /\.create\s*\(/,
15
+ /\.submit\s*\(/,
16
+ ];
17
+ const UI_UPDATE_PATTERNS = [
18
+ /\.innerHTML\s*=/,
19
+ /\.textContent\s*=/,
20
+ /\.innerText\s*=/,
21
+ /\.classList\./,
22
+ /\.style\./,
23
+ /\.setAttribute\s*\(/,
24
+ /\.appendChild\s*\(/,
25
+ /\.replaceChild\s*\(/,
26
+ /\.removeChild\s*\(/,
27
+ /\.insertBefore\s*\(/,
28
+ /setState\s*\(/,
29
+ /\.render\s*\(/,
30
+ /\.forceUpdate\s*\(/,
31
+ /dispatchEvent\s*\(/,
32
+ /\.value\s*=/,
33
+ /document\.querySelector/,
34
+ /\.replaceWith\s*\(/,
35
+ ];
36
+ export const BP_005 = {
37
+ id: 'BP-005',
38
+ category: 'best-practices',
39
+ name: 'UI Sync After Execution',
40
+ description: 'Execute functions should update the DOM/UI after state changes',
41
+ severity: 'warning',
42
+ maxScore: 5,
43
+ async check(context) {
44
+ const tools = context.tools;
45
+ if (tools.length === 0) {
46
+ return createRuleResult('BP-005', 5, {
47
+ passed: true,
48
+ score: 5,
49
+ message: 'No tools detected (rule not applicable)',
50
+ });
51
+ }
52
+ const toolsWithExecute = tools.filter((t) => t.executeSource && t.executeSource.length > 0);
53
+ if (toolsWithExecute.length === 0) {
54
+ return createRuleResult('BP-005', 5, {
55
+ passed: true,
56
+ score: 5,
57
+ message: 'No execute functions available for analysis',
58
+ });
59
+ }
60
+ const violations = [];
61
+ for (const tool of toolsWithExecute) {
62
+ const src = tool.executeSource ?? '';
63
+ const hasStateChange = STATE_CHANGE_PATTERNS.some((p) => p.test(src));
64
+ if (!hasStateChange)
65
+ continue;
66
+ const hasUIUpdate = UI_UPDATE_PATTERNS.some((p) => p.test(src));
67
+ if (!hasUIUpdate) {
68
+ violations.push(tool.name);
69
+ }
70
+ }
71
+ if (violations.length === 0) {
72
+ return createRuleResult('BP-005', 5, {
73
+ passed: true,
74
+ score: 5,
75
+ message: 'Execute functions update UI after state changes',
76
+ });
77
+ }
78
+ const penalty = Math.min(violations.length, 5);
79
+ const score = Math.max(0, 5 - penalty);
80
+ return createRuleResult('BP-005', 5, {
81
+ passed: false,
82
+ score,
83
+ message: `${violations.length} tool(s) change state without updating UI`,
84
+ details: violations.map((name) => `Tool "${name}" modifies state (POST/save/update) but has no visible DOM update`),
85
+ suggestions: [
86
+ 'Update the DOM to reflect state changes after execute completes',
87
+ 'Use dispatchEvent to notify UI components of data changes',
88
+ 'Ensure the user sees confirmation of the tool\'s action in the page',
89
+ ],
90
+ affectedTools: violations,
91
+ });
92
+ },
93
+ };
94
+ //# sourceMappingURL=BP-005.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BP-005.js","sourceRoot":"","sources":["../../../src/rules/best-practices/BP-005.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * BP-006: Returns After UI Update
3
+ *
4
+ * Checks that execute functions return after the UI has been updated,
5
+ * not before. The return value should reflect the final state.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const BP_006: Rule;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * BP-006: Returns After UI Update
3
+ *
4
+ * Checks that execute functions return after the UI has been updated,
5
+ * not before. The return value should reflect the final state.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ const ASYNC_AWAIT_PATTERN = /\basync\b/;
9
+ const EARLY_RETURN_PATTERN = /return\s+[^;]*;\s*(?:.*(?:fetch|\.then|await)\s*\()/s;
10
+ const FIRE_AND_FORGET_PATTERN = /(?:fetch|\.send|\.post)\s*\([^)]*\)\s*;?\s*\n\s*return\b/;
11
+ export const BP_006 = {
12
+ id: 'BP-006',
13
+ category: 'best-practices',
14
+ name: 'Returns After UI Update',
15
+ description: 'Execute functions should return after the UI has been updated, not before',
16
+ severity: 'info',
17
+ maxScore: 5,
18
+ async check(context) {
19
+ const tools = context.tools;
20
+ if (tools.length === 0) {
21
+ return createRuleResult('BP-006', 5, {
22
+ passed: true,
23
+ score: 5,
24
+ message: 'No tools detected (rule not applicable)',
25
+ });
26
+ }
27
+ const toolsWithExecute = tools.filter((t) => t.executeSource && t.executeSource.length > 0);
28
+ if (toolsWithExecute.length === 0) {
29
+ return createRuleResult('BP-006', 5, {
30
+ passed: true,
31
+ score: 5,
32
+ message: 'No execute functions available for analysis',
33
+ });
34
+ }
35
+ const violations = [];
36
+ for (const tool of toolsWithExecute) {
37
+ const src = tool.executeSource ?? '';
38
+ // Check for fire-and-forget patterns (fetch then immediate return)
39
+ if (FIRE_AND_FORGET_PATTERN.test(src)) {
40
+ violations.push({
41
+ name: tool.name,
42
+ issue: 'Returns immediately after firing async request (fire-and-forget)',
43
+ });
44
+ continue;
45
+ }
46
+ // If async function, check that it awaits before returning
47
+ if (ASYNC_AWAIT_PATTERN.test(src)) {
48
+ // Check for patterns where return comes before async work
49
+ if (EARLY_RETURN_PATTERN.test(src)) {
50
+ violations.push({
51
+ name: tool.name,
52
+ issue: 'Returns before async operations complete',
53
+ });
54
+ }
55
+ }
56
+ }
57
+ if (violations.length === 0) {
58
+ return createRuleResult('BP-006', 5, {
59
+ passed: true,
60
+ score: 5,
61
+ message: 'Execute functions return after UI updates complete',
62
+ });
63
+ }
64
+ const penalty = Math.min(violations.length, 5);
65
+ const score = Math.max(0, 5 - penalty);
66
+ return createRuleResult('BP-006', 5, {
67
+ passed: false,
68
+ score,
69
+ message: `${violations.length} tool(s) may return before UI is updated`,
70
+ details: violations.map((v) => `Tool "${v.name}": ${v.issue}`),
71
+ suggestions: [
72
+ 'Use await to ensure async operations complete before returning',
73
+ 'Return after the DOM/UI has been updated to reflect the new state',
74
+ 'Avoid fire-and-forget patterns — agents rely on return values for next steps',
75
+ ],
76
+ affectedTools: violations.map((v) => v.name),
77
+ });
78
+ },
79
+ };
80
+ //# sourceMappingURL=BP-006.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BP-006.js","sourceRoot":"","sources":["../../../src/rules/best-practices/BP-006.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * BP-007: Graceful Rate Limit
3
+ *
4
+ * Checks that tools handle rate limiting with meaningful error messages
5
+ * rather than generic failures.
6
+ */
7
+ import type { Rule } from '../../core/types/rule.js';
8
+ export declare const BP_007: Rule;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * BP-007: Graceful Rate Limit
3
+ *
4
+ * Checks that tools handle rate limiting with meaningful error messages
5
+ * rather than generic failures.
6
+ */
7
+ import { createRuleResult } from '../runner.js';
8
+ const RATE_LIMIT_STATUS_PATTERNS = [
9
+ /429/,
10
+ /rate.?limit/i,
11
+ /too.?many.?requests/i,
12
+ /throttl/i,
13
+ ];
14
+ const GRACEFUL_HANDLING_PATTERNS = [
15
+ /retry.?after/i,
16
+ /retryAfter/i,
17
+ /wait/i,
18
+ /backoff/i,
19
+ /cooldown/i,
20
+ /rate.?limit.*message/i,
21
+ /too.?many.*try.?again/i,
22
+ ];
23
+ export const BP_007 = {
24
+ id: 'BP-007',
25
+ category: 'best-practices',
26
+ name: 'Graceful Rate Limit',
27
+ description: 'Tools should handle rate limiting with meaningful errors and retry guidance',
28
+ severity: 'info',
29
+ maxScore: 5,
30
+ async check(context) {
31
+ const tools = context.tools;
32
+ if (tools.length === 0) {
33
+ return createRuleResult('BP-007', 5, {
34
+ passed: true,
35
+ score: 5,
36
+ message: 'No tools detected (rule not applicable)',
37
+ });
38
+ }
39
+ const toolsWithExecute = tools.filter((t) => t.executeSource && t.executeSource.length > 0);
40
+ if (toolsWithExecute.length === 0) {
41
+ return createRuleResult('BP-007', 5, {
42
+ passed: true,
43
+ score: 5,
44
+ message: 'No execute functions available for analysis',
45
+ });
46
+ }
47
+ const violations = [];
48
+ let toolsWithFetch = 0;
49
+ for (const tool of toolsWithExecute) {
50
+ const src = tool.executeSource ?? '';
51
+ // Only relevant for tools that make network requests
52
+ const makesFetchCalls = /fetch\s*\(|XMLHttpRequest|\.ajax\s*\(/.test(src);
53
+ if (!makesFetchCalls)
54
+ continue;
55
+ toolsWithFetch++;
56
+ const checksRateLimit = RATE_LIMIT_STATUS_PATTERNS.some((p) => p.test(src));
57
+ const handlesGracefully = GRACEFUL_HANDLING_PATTERNS.some((p) => p.test(src));
58
+ if (!checksRateLimit && !handlesGracefully) {
59
+ violations.push(tool.name);
60
+ }
61
+ }
62
+ if (toolsWithFetch === 0) {
63
+ return createRuleResult('BP-007', 5, {
64
+ passed: true,
65
+ score: 5,
66
+ message: 'No network-calling tools detected (rule not applicable)',
67
+ });
68
+ }
69
+ if (violations.length === 0) {
70
+ return createRuleResult('BP-007', 5, {
71
+ passed: true,
72
+ score: 5,
73
+ message: 'Network-calling tools handle rate limiting gracefully',
74
+ });
75
+ }
76
+ const penalty = Math.min(violations.length, 5);
77
+ const score = Math.max(0, 5 - penalty);
78
+ return createRuleResult('BP-007', 5, {
79
+ passed: false,
80
+ score,
81
+ message: `${violations.length} tool(s) don't handle rate limiting gracefully`,
82
+ details: violations.map((name) => `Tool "${name}" makes network requests but doesn't handle 429/rate-limit responses`),
83
+ suggestions: [
84
+ 'Check for HTTP 429 responses and provide meaningful error messages',
85
+ 'Include retry-after timing in error responses when rate-limited',
86
+ 'Return { error: true, retryable: true, retryAfter: seconds } for rate limits',
87
+ ],
88
+ affectedTools: violations,
89
+ });
90
+ },
91
+ };
92
+ //# sourceMappingURL=BP-007.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BP-007.js","sourceRoot":"","sources":["../../../src/rules/best-practices/BP-007.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * BP-008: State-Appropriate Tools
3
+ *
4
+ * Checks that tools are only registered when they make sense for
5
+ * the current UI state (e.g., a "submit order" tool shouldn't be
6
+ * registered before the cart has items).
7
+ *
8
+ * This is a heuristic check — looks for tools with state-dependent names
9
+ * that are registered at page load rather than dynamically.
10
+ */
11
+ import type { Rule } from '../../core/types/rule.js';
12
+ export declare const BP_008: Rule;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * BP-008: State-Appropriate Tools
3
+ *
4
+ * Checks that tools are only registered when they make sense for
5
+ * the current UI state (e.g., a "submit order" tool shouldn't be
6
+ * registered before the cart has items).
7
+ *
8
+ * This is a heuristic check — looks for tools with state-dependent names
9
+ * that are registered at page load rather than dynamically.
10
+ */
11
+ import { createRuleResult } from '../runner.js';
12
+ const STATE_DEPENDENT_KEYWORDS = [
13
+ 'submit',
14
+ 'confirm',
15
+ 'checkout',
16
+ 'finalize',
17
+ 'complete',
18
+ 'next step',
19
+ 'proceed',
20
+ 'approve',
21
+ 'publish',
22
+ 'deploy',
23
+ ];
24
+ export const BP_008 = {
25
+ id: 'BP-008',
26
+ category: 'best-practices',
27
+ name: 'State-Appropriate Tools',
28
+ description: 'Tools should be registered only when they make sense for the current UI state',
29
+ severity: 'info',
30
+ maxScore: 5,
31
+ async check(context) {
32
+ const tools = context.tools;
33
+ if (tools.length === 0) {
34
+ return createRuleResult('BP-008', 5, {
35
+ passed: true,
36
+ score: 5,
37
+ message: 'No tools detected (rule not applicable)',
38
+ });
39
+ }
40
+ // Only check imperative tools (declarative forms are always in the DOM)
41
+ const imperativeTools = tools.filter((t) => t.source === 'imperative');
42
+ if (imperativeTools.length === 0) {
43
+ return createRuleResult('BP-008', 5, {
44
+ passed: true,
45
+ score: 5,
46
+ message: 'No imperative tools detected (rule not applicable)',
47
+ });
48
+ }
49
+ const warnings = [];
50
+ for (const tool of imperativeTools) {
51
+ const nameAndDesc = `${tool.name} ${tool.description}`.toLowerCase();
52
+ const isStateDep = STATE_DEPENDENT_KEYWORDS.some((kw) => nameAndDesc.includes(kw));
53
+ if (!isStateDep)
54
+ continue;
55
+ // Check if the tool's execute source has state guards
56
+ const src = tool.executeSource ?? '';
57
+ const hasStateGuard = /if\s*\(/.test(src) &&
58
+ (/disabled|enabled|ready|valid|empty|length\s*[=><!]|\.items|\.count/.test(src));
59
+ if (!hasStateGuard) {
60
+ warnings.push(tool.name);
61
+ }
62
+ }
63
+ if (warnings.length === 0) {
64
+ return createRuleResult('BP-008', 5, {
65
+ passed: true,
66
+ score: 5,
67
+ message: 'State-dependent tools have appropriate guards',
68
+ });
69
+ }
70
+ const penalty = Math.min(warnings.length, 5);
71
+ const score = Math.max(0, 5 - penalty);
72
+ return createRuleResult('BP-008', 5, {
73
+ passed: false,
74
+ score,
75
+ message: `${warnings.length} tool(s) may be registered before the UI state supports them`,
76
+ details: warnings.map((name) => `Tool "${name}" has a state-dependent name but no visible state guard in execute`),
77
+ suggestions: [
78
+ 'Register tools dynamically when their preconditions are met',
79
+ 'Add state validation at the start of execute functions',
80
+ 'Use navigator.modelContext.unregisterTool() when a tool becomes invalid',
81
+ ],
82
+ affectedTools: warnings,
83
+ });
84
+ },
85
+ };
86
+ //# sourceMappingURL=BP-008.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BP-008.js","sourceRoot":"","sources":["../../../src/rules/best-practices/BP-008.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * BP-009: No Orphaned Tools
3
+ *
4
+ * Checks that all registered tools are reachable through some UI path.
5
+ * An orphaned tool is one that exists in the MCP registry but has no
6
+ * corresponding UI element on the page.
7
+ */
8
+ import type { Rule } from '../../core/types/rule.js';
9
+ export declare const BP_009: Rule;