glirastes 0.3.0 → 0.3.1

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 (310) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +146 -9
  3. package/codegen/package.json +5 -0
  4. package/dist/cjs/codegen/asset-sync.d.ts +20 -0
  5. package/dist/cjs/codegen/asset-sync.js +28 -0
  6. package/dist/cjs/codegen/coverage.d.ts +66 -0
  7. package/dist/cjs/codegen/coverage.js +408 -0
  8. package/dist/cjs/codegen/endpoint-scanner.d.ts +16 -0
  9. package/dist/cjs/codegen/endpoint-scanner.js +197 -0
  10. package/dist/cjs/codegen/format.d.ts +17 -0
  11. package/dist/cjs/codegen/format.js +34 -0
  12. package/dist/cjs/codegen/fs-utils.d.ts +45 -0
  13. package/dist/cjs/codegen/fs-utils.js +165 -0
  14. package/dist/cjs/codegen/index.d.ts +13 -0
  15. package/dist/cjs/codegen/index.js +33 -0
  16. package/dist/cjs/codegen/mcp-generator.d.ts +50 -0
  17. package/dist/cjs/codegen/mcp-generator.js +803 -0
  18. package/dist/cjs/codegen/module-generator.d.ts +11 -0
  19. package/dist/cjs/codegen/module-generator.js +353 -0
  20. package/dist/cjs/codegen/nestjs-scanner.d.ts +43 -0
  21. package/dist/cjs/codegen/nestjs-scanner.js +241 -0
  22. package/dist/cjs/codegen/output-recommender.d.ts +23 -0
  23. package/dist/cjs/codegen/output-recommender.js +33 -0
  24. package/dist/cjs/codegen/scaffolder.d.ts +36 -0
  25. package/dist/cjs/codegen/scaffolder.js +262 -0
  26. package/dist/cjs/codegen/skill-generator.d.ts +49 -0
  27. package/dist/cjs/codegen/skill-generator.js +967 -0
  28. package/dist/cjs/codegen/types.d.ts +52 -0
  29. package/dist/cjs/codegen/types.js +16 -0
  30. package/dist/cjs/codegen/ui-scanner.d.ts +24 -0
  31. package/dist/cjs/codegen/ui-scanner.js +202 -0
  32. package/dist/cjs/codegen/validator.d.ts +35 -0
  33. package/dist/cjs/codegen/validator.js +292 -0
  34. package/dist/cjs/index.d.ts +1 -0
  35. package/dist/cjs/index.js +23 -0
  36. package/dist/cjs/openapi/index.d.ts +78 -0
  37. package/dist/cjs/openapi/index.js +347 -0
  38. package/dist/cjs/package.json +1 -0
  39. package/dist/cjs/react/audio-worklet-processor.d.ts +16 -0
  40. package/dist/cjs/react/audio-worklet-processor.js +27 -0
  41. package/dist/cjs/react/bridges/rest-session-bridge.d.ts +113 -0
  42. package/dist/cjs/react/bridges/rest-session-bridge.js +209 -0
  43. package/dist/cjs/react/components/ai-chat-panel.d.ts +12 -0
  44. package/dist/cjs/react/components/ai-chat-panel.js +67 -0
  45. package/dist/cjs/react/components/ai-trigger-button.d.ts +2 -0
  46. package/dist/cjs/react/components/ai-trigger-button.js +239 -0
  47. package/dist/cjs/react/components/approval-card.d.ts +9 -0
  48. package/dist/cjs/react/components/approval-card.js +24 -0
  49. package/dist/cjs/react/components/bulk-approval-card.d.ts +9 -0
  50. package/dist/cjs/react/components/bulk-approval-card.js +36 -0
  51. package/dist/cjs/react/components/chat-input.d.ts +10 -0
  52. package/dist/cjs/react/components/chat-input.js +60 -0
  53. package/dist/cjs/react/components/chat-window.d.ts +2 -0
  54. package/dist/cjs/react/components/chat-window.js +134 -0
  55. package/dist/cjs/react/components/confirm-dialog.d.ts +32 -0
  56. package/dist/cjs/react/components/confirm-dialog.js +48 -0
  57. package/dist/cjs/react/components/floating-chat-ui.d.ts +90 -0
  58. package/dist/cjs/react/components/floating-chat-ui.js +163 -0
  59. package/dist/cjs/react/components/mention-chip.d.ts +16 -0
  60. package/dist/cjs/react/components/mention-chip.js +42 -0
  61. package/dist/cjs/react/components/mention-input.d.ts +8 -0
  62. package/dist/cjs/react/components/mention-input.js +117 -0
  63. package/dist/cjs/react/components/message-bubble.d.ts +11 -0
  64. package/dist/cjs/react/components/message-bubble.js +171 -0
  65. package/dist/cjs/react/components/message-list.d.ts +8 -0
  66. package/dist/cjs/react/components/message-list.js +52 -0
  67. package/dist/cjs/react/components/pipeline-timeline.d.ts +7 -0
  68. package/dist/cjs/react/components/pipeline-timeline.js +72 -0
  69. package/dist/cjs/react/components/recording-bar.d.ts +2 -0
  70. package/dist/cjs/react/components/recording-bar.js +81 -0
  71. package/dist/cjs/react/components/recording-controls.d.ts +18 -0
  72. package/dist/cjs/react/components/recording-controls.js +38 -0
  73. package/dist/cjs/react/components/rich-mention-input.d.ts +2 -0
  74. package/dist/cjs/react/components/rich-mention-input.js +571 -0
  75. package/dist/cjs/react/components/session-switcher.d.ts +14 -0
  76. package/dist/cjs/react/components/session-switcher.js +133 -0
  77. package/dist/cjs/react/components/suggestion-bar.d.ts +9 -0
  78. package/dist/cjs/react/components/suggestion-bar.js +47 -0
  79. package/dist/cjs/react/components/suggestion-chip.d.ts +9 -0
  80. package/dist/cjs/react/components/suggestion-chip.js +14 -0
  81. package/dist/cjs/react/components/voice-input-addon.d.ts +13 -0
  82. package/dist/cjs/react/components/voice-input-addon.js +31 -0
  83. package/dist/cjs/react/components/voice-input-button.d.ts +2 -0
  84. package/dist/cjs/react/components/voice-input-button.js +55 -0
  85. package/dist/cjs/react/components/wavesurfer-loader.d.ts +5 -0
  86. package/dist/cjs/react/components/wavesurfer-loader.js +36 -0
  87. package/dist/cjs/react/hooks/use-ai-chat.d.ts +22 -0
  88. package/dist/cjs/react/hooks/use-ai-chat.js +51 -0
  89. package/dist/cjs/react/hooks/use-approvals.d.ts +23 -0
  90. package/dist/cjs/react/hooks/use-approvals.js +63 -0
  91. package/dist/cjs/react/hooks/use-deepgram-transcription.d.ts +2 -0
  92. package/dist/cjs/react/hooks/use-deepgram-transcription.js +329 -0
  93. package/dist/cjs/react/hooks/use-drag-resize-panel.d.ts +48 -0
  94. package/dist/cjs/react/hooks/use-drag-resize-panel.js +308 -0
  95. package/dist/cjs/react/hooks/use-draggable-position.d.ts +25 -0
  96. package/dist/cjs/react/hooks/use-draggable-position.js +134 -0
  97. package/dist/cjs/react/hooks/use-keyboard-shortcut.d.ts +20 -0
  98. package/dist/cjs/react/hooks/use-keyboard-shortcut.js +93 -0
  99. package/dist/cjs/react/hooks/use-mentions.d.ts +23 -0
  100. package/dist/cjs/react/hooks/use-mentions.js +42 -0
  101. package/dist/cjs/react/hooks/use-suggestions.d.ts +22 -0
  102. package/dist/cjs/react/hooks/use-suggestions.js +39 -0
  103. package/dist/cjs/react/index.d.ts +43 -0
  104. package/dist/cjs/react/index.js +111 -0
  105. package/dist/cjs/react/provider/ai-chat-provider.d.ts +45 -0
  106. package/dist/cjs/react/provider/ai-chat-provider.js +1024 -0
  107. package/dist/cjs/react/provider/chat-context.d.ts +84 -0
  108. package/dist/cjs/react/provider/chat-context.js +13 -0
  109. package/dist/cjs/react/template.d.ts +84 -0
  110. package/dist/cjs/react/template.js +90 -0
  111. package/dist/cjs/react/themes.d.ts +24 -0
  112. package/dist/cjs/react/themes.js +273 -0
  113. package/dist/cjs/react/transports/langgraph/index.d.ts +2 -0
  114. package/dist/cjs/react/transports/langgraph/index.js +8 -0
  115. package/dist/cjs/react/transports/langgraph/langgraph-ai-chat.d.ts +62 -0
  116. package/dist/cjs/react/transports/langgraph/langgraph-ai-chat.js +94 -0
  117. package/dist/cjs/react/transports/langgraph.d.ts +71 -0
  118. package/dist/cjs/react/transports/langgraph.js +369 -0
  119. package/dist/cjs/react/transports/vercel/index.d.ts +2 -0
  120. package/dist/cjs/react/transports/vercel/index.js +8 -0
  121. package/dist/cjs/react/transports/vercel/vercel-ai-chat.d.ts +49 -0
  122. package/dist/cjs/react/transports/vercel/vercel-ai-chat.js +81 -0
  123. package/dist/cjs/react/transports/vercel.d.ts +30 -0
  124. package/dist/cjs/react/transports/vercel.js +69 -0
  125. package/dist/cjs/react/types.d.ts +760 -0
  126. package/dist/cjs/react/types.js +2 -0
  127. package/dist/cjs/react/ui.d.ts +73 -0
  128. package/dist/cjs/react/ui.js +183 -0
  129. package/dist/cjs/react/utils/create-approval-description.d.ts +53 -0
  130. package/dist/cjs/react/utils/create-approval-description.js +122 -0
  131. package/dist/cjs/react/utils/create-tool-result-handler.d.ts +90 -0
  132. package/dist/cjs/react/utils/create-tool-result-handler.js +168 -0
  133. package/dist/cjs/react/utils/mention-markup.d.ts +13 -0
  134. package/dist/cjs/react/utils/mention-markup.js +117 -0
  135. package/dist/cjs/react/utils/merge-pipeline-steps.d.ts +19 -0
  136. package/dist/cjs/react/utils/merge-pipeline-steps.js +81 -0
  137. package/dist/cjs/react/utils/sanitize-stored-messages.d.ts +23 -0
  138. package/dist/cjs/react/utils/sanitize-stored-messages.js +52 -0
  139. package/dist/cjs/server/adapters/nestjs/chat/ai-chat-auth.guard.d.ts +10 -0
  140. package/dist/cjs/server/adapters/nestjs/chat/ai-chat-auth.guard.js +42 -0
  141. package/dist/cjs/server/adapters/nestjs/chat/ai-chat-module-options.interface.d.ts +32 -0
  142. package/dist/cjs/server/adapters/nestjs/chat/ai-chat-module-options.interface.js +4 -0
  143. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.controller.d.ts +11 -0
  144. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.controller.js +80 -0
  145. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.module.d.ts +9 -0
  146. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.module.js +119 -0
  147. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.service.d.ts +13 -0
  148. package/dist/cjs/server/adapters/nestjs/chat/ai-chat.service.js +131 -0
  149. package/dist/cjs/server/adapters/nestjs/chat/dto/ai-chat-request.dto.d.ts +7 -0
  150. package/dist/cjs/server/adapters/nestjs/chat/dto/ai-chat-request.dto.js +52 -0
  151. package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message-part.dto.d.ts +19 -0
  152. package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message-part.dto.js +108 -0
  153. package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message.dto.d.ts +8 -0
  154. package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message.dto.js +59 -0
  155. package/dist/cjs/server/adapters/nestjs/chat/dto/index.d.ts +3 -0
  156. package/dist/cjs/server/adapters/nestjs/chat/dto/index.js +9 -0
  157. package/dist/cjs/server/adapters/nestjs/chat/index.d.ts +3 -0
  158. package/dist/cjs/server/adapters/nestjs/chat/index.js +9 -0
  159. package/dist/cjs/server/adapters/nestjs/chat/speech-stream.gateway.d.ts +4 -0
  160. package/dist/cjs/server/adapters/nestjs/chat/speech-stream.gateway.js +73 -0
  161. package/dist/cjs/server/adapters/nestjs/chat/ws-loader.d.ts +5 -0
  162. package/dist/cjs/server/adapters/nestjs/chat/ws-loader.js +72 -0
  163. package/dist/cjs/server/adapters/nestjs/decorators/ai-module.decorator.d.ts +6 -0
  164. package/dist/cjs/server/adapters/nestjs/decorators/ai-module.decorator.js +13 -0
  165. package/dist/cjs/server/adapters/nestjs/decorators/ai-param.decorator.d.ts +5 -0
  166. package/dist/cjs/server/adapters/nestjs/decorators/ai-param.decorator.js +15 -0
  167. package/dist/cjs/server/adapters/nestjs/decorators/ai-tool.decorator.d.ts +6 -0
  168. package/dist/cjs/server/adapters/nestjs/decorators/ai-tool.decorator.js +22 -0
  169. package/dist/cjs/server/adapters/nestjs/decorators/constants.d.ts +4 -0
  170. package/dist/cjs/server/adapters/nestjs/decorators/constants.js +7 -0
  171. package/dist/cjs/server/adapters/nestjs/index.d.ts +14 -0
  172. package/dist/cjs/server/adapters/nestjs/index.js +42 -0
  173. package/dist/cjs/server/adapters/nestjs/metadata.d.ts +39 -0
  174. package/dist/cjs/server/adapters/nestjs/metadata.js +36 -0
  175. package/dist/cjs/server/adapters/nestjs/module/ai-tools-explorer.service.d.ts +36 -0
  176. package/dist/cjs/server/adapters/nestjs/module/ai-tools-explorer.service.js +159 -0
  177. package/dist/cjs/server/adapters/nestjs/module/ai-tools.module.d.ts +37 -0
  178. package/dist/cjs/server/adapters/nestjs/module/ai-tools.module.js +44 -0
  179. package/dist/cjs/server/adapters/nestjs/module/ai-tools.service.d.ts +28 -0
  180. package/dist/cjs/server/adapters/nestjs/module/ai-tools.service.js +41 -0
  181. package/dist/cjs/server/adapters/nestjs/scanner/nestjs-scanner.d.ts +16 -0
  182. package/dist/cjs/server/adapters/nestjs/scanner/nestjs-scanner.js +117 -0
  183. package/dist/cjs/server/adapters/nestjs/scanner/schema-builder.d.ts +26 -0
  184. package/dist/cjs/server/adapters/nestjs/scanner/schema-builder.js +259 -0
  185. package/dist/cjs/server/adapters/nestjs/swagger.d.ts +7 -0
  186. package/dist/cjs/server/adapters/nestjs/swagger.js +39 -0
  187. package/dist/cjs/server/adapters/nextjs/context-helpers.d.ts +22 -0
  188. package/dist/cjs/server/adapters/nextjs/context-helpers.js +60 -0
  189. package/dist/cjs/server/adapters/nextjs/create-ai-chat-handler.d.ts +94 -0
  190. package/dist/cjs/server/adapters/nextjs/create-ai-chat-handler.js +965 -0
  191. package/dist/cjs/server/adapters/nextjs/filter-fallback-error.d.ts +11 -0
  192. package/dist/cjs/server/adapters/nextjs/filter-fallback-error.js +41 -0
  193. package/dist/cjs/server/adapters/nextjs/index.d.ts +8 -0
  194. package/dist/cjs/server/adapters/nextjs/index.js +22 -0
  195. package/dist/cjs/server/adapters/nextjs/prepare-model-messages.d.ts +58 -0
  196. package/dist/cjs/server/adapters/nextjs/prepare-model-messages.js +114 -0
  197. package/dist/cjs/server/adapters/nextjs/sanitize-model-messages.d.ts +64 -0
  198. package/dist/cjs/server/adapters/nextjs/sanitize-model-messages.js +489 -0
  199. package/dist/cjs/server/adapters/nextjs/server-action-helper.d.ts +7 -0
  200. package/dist/cjs/server/adapters/nextjs/server-action-helper.js +40 -0
  201. package/dist/cjs/server/adapters/nextjs/types.d.ts +246 -0
  202. package/dist/cjs/server/adapters/nextjs/types.js +2 -0
  203. package/dist/cjs/server/adapters/nextjs/with-ai-tool.d.ts +9 -0
  204. package/dist/cjs/server/adapters/nextjs/with-ai-tool.js +12 -0
  205. package/dist/cjs/server/core/followup-tool.d.ts +9 -0
  206. package/dist/cjs/server/core/followup-tool.js +82 -0
  207. package/dist/cjs/server/core/index.d.ts +139 -0
  208. package/dist/cjs/server/core/index.js +818 -0
  209. package/dist/cjs/server/core/intent-classifier.d.ts +94 -0
  210. package/dist/cjs/server/core/intent-classifier.js +324 -0
  211. package/dist/cjs/server/core/model-selector.d.ts +12 -0
  212. package/dist/cjs/server/core/model-selector.js +48 -0
  213. package/dist/cjs/server/core/module-router.d.ts +14 -0
  214. package/dist/cjs/server/core/module-router.js +110 -0
  215. package/dist/cjs/server/core/zod-compact.d.ts +18 -0
  216. package/dist/cjs/server/core/zod-compact.js +129 -0
  217. package/dist/cjs/server/index.d.ts +6 -0
  218. package/dist/cjs/server/index.js +28 -0
  219. package/dist/cjs/server/lancer/cache.d.ts +9 -0
  220. package/dist/cjs/server/lancer/cache.js +33 -0
  221. package/dist/cjs/server/lancer/client.d.ts +2 -0
  222. package/dist/cjs/server/lancer/client.js +279 -0
  223. package/dist/cjs/server/lancer/errors.d.ts +13 -0
  224. package/dist/cjs/server/lancer/errors.js +21 -0
  225. package/dist/cjs/server/lancer/index.d.ts +3 -0
  226. package/dist/cjs/server/lancer/index.js +7 -0
  227. package/dist/cjs/server/lancer/telemetry-buffer.d.ts +15 -0
  228. package/dist/cjs/server/lancer/telemetry-buffer.js +64 -0
  229. package/dist/cjs/server/lancer/types.d.ts +201 -0
  230. package/dist/cjs/server/lancer/types.js +5 -0
  231. package/dist/cjs/server/pii-shield/aegis-detector.d.ts +26 -0
  232. package/dist/cjs/server/pii-shield/aegis-detector.js +26 -0
  233. package/dist/cjs/server/pii-shield/create-pii-shield.d.ts +2 -0
  234. package/dist/cjs/server/pii-shield/create-pii-shield.js +191 -0
  235. package/dist/cjs/server/pii-shield/deep-walk.d.ts +1 -0
  236. package/dist/cjs/server/pii-shield/deep-walk.js +19 -0
  237. package/dist/cjs/server/pii-shield/fake-data.d.ts +5 -0
  238. package/dist/cjs/server/pii-shield/fake-data.js +170 -0
  239. package/dist/cjs/server/pii-shield/index.d.ts +4 -0
  240. package/dist/cjs/server/pii-shield/index.js +9 -0
  241. package/dist/cjs/server/pii-shield/leakage.d.ts +1 -0
  242. package/dist/cjs/server/pii-shield/leakage.js +15 -0
  243. package/dist/cjs/server/pii-shield/session-mapping.d.ts +12 -0
  244. package/dist/cjs/server/pii-shield/session-mapping.js +70 -0
  245. package/dist/cjs/server/pipeline.d.ts +26 -0
  246. package/dist/cjs/server/pipeline.js +127 -0
  247. package/dist/cjs/server/testing/approvals.d.ts +11 -0
  248. package/dist/cjs/server/testing/approvals.js +41 -0
  249. package/dist/cjs/server/testing/combination.d.ts +3 -0
  250. package/dist/cjs/server/testing/combination.js +83 -0
  251. package/dist/cjs/server/testing/create-test-suite.d.ts +29 -0
  252. package/dist/cjs/server/testing/create-test-suite.js +81 -0
  253. package/dist/cjs/server/testing/edge-cases.d.ts +8 -0
  254. package/dist/cjs/server/testing/edge-cases.js +277 -0
  255. package/dist/cjs/server/testing/guardrails.d.ts +14 -0
  256. package/dist/cjs/server/testing/guardrails.js +152 -0
  257. package/dist/cjs/server/testing/helpers.d.ts +14 -0
  258. package/dist/cjs/server/testing/helpers.js +119 -0
  259. package/dist/cjs/server/testing/index.d.ts +12 -0
  260. package/dist/cjs/server/testing/index.js +37 -0
  261. package/dist/cjs/server/testing/mock-executor.d.ts +8 -0
  262. package/dist/cjs/server/testing/mock-executor.js +40 -0
  263. package/dist/cjs/server/testing/regression.d.ts +7 -0
  264. package/dist/cjs/server/testing/regression.js +27 -0
  265. package/dist/cjs/server/testing/routing.d.ts +12 -0
  266. package/dist/cjs/server/testing/routing.js +63 -0
  267. package/dist/cjs/server/testing/schema-consistency.d.ts +6 -0
  268. package/dist/cjs/server/testing/schema-consistency.js +56 -0
  269. package/dist/cjs/server/testing/smoke.d.ts +11 -0
  270. package/dist/cjs/server/testing/smoke.js +64 -0
  271. package/dist/cjs/server/testing/types.d.ts +156 -0
  272. package/dist/cjs/server/testing/types.js +2 -0
  273. package/dist/cjs/types.d.ts +696 -0
  274. package/dist/cjs/types.js +91 -0
  275. package/dist/cli/commands/install-skills.d.ts +17 -0
  276. package/dist/cli/commands/install-skills.js +185 -0
  277. package/dist/cli/commands/install-skills.js.map +1 -0
  278. package/dist/cli/index.js +25 -0
  279. package/dist/cli/index.js.map +1 -1
  280. package/dist/react/components/ai-trigger-button.js +4 -6
  281. package/dist/react/components/ai-trigger-button.js.map +1 -1
  282. package/dist/react/components/recording-bar.js +4 -6
  283. package/dist/react/components/recording-bar.js.map +1 -1
  284. package/dist/react/components/wavesurfer-loader.d.ts +5 -0
  285. package/dist/react/components/wavesurfer-loader.js +34 -0
  286. package/dist/react/components/wavesurfer-loader.js.map +1 -0
  287. package/dist/server/adapters/nestjs/chat/ai-chat.module.d.ts +1 -1
  288. package/dist/server/adapters/nestjs/chat/ai-chat.module.js +3 -2
  289. package/dist/server/adapters/nestjs/chat/ai-chat.module.js.map +1 -1
  290. package/dist/server/adapters/nestjs/chat/speech-stream.gateway.d.ts +1 -1
  291. package/dist/server/adapters/nestjs/chat/speech-stream.gateway.js +10 -8
  292. package/dist/server/adapters/nestjs/chat/speech-stream.gateway.js.map +1 -1
  293. package/dist/server/adapters/nestjs/chat/ws-loader.d.ts +5 -0
  294. package/dist/server/adapters/nestjs/chat/ws-loader.js +37 -0
  295. package/dist/server/adapters/nestjs/chat/ws-loader.js.map +1 -0
  296. package/openapi/package.json +5 -0
  297. package/package.json +59 -16
  298. package/react/langgraph/package.json +5 -0
  299. package/react/package.json +5 -0
  300. package/react/vercel/package.json +5 -0
  301. package/server/nestjs/package.json +5 -0
  302. package/server/nextjs/package.json +5 -0
  303. package/server/package.json +5 -0
  304. package/server/testing/package.json +5 -0
  305. package/skills/README.md +107 -0
  306. package/skills/building-glirastes-chat-ui/SKILL.md +378 -0
  307. package/skills/integrating-glirastes-nestjs/SKILL.md +346 -0
  308. package/skills/integrating-glirastes-nextjs/SKILL.md +306 -0
  309. package/skills/integrating-glirastes-nextjs-with-nestjs-backend/SKILL.md +359 -0
  310. package/skills/maintaining-glirastes-tools/SKILL.md +463 -0
@@ -0,0 +1,11 @@
1
+ import type { GenerateOptions } from './types.js';
2
+ /**
3
+ * Generate the modules registry file.
4
+ *
5
+ * Groups all tools by their module (including sharedWith expansion),
6
+ * and generates a mapping from module → toolNames[].
7
+ */
8
+ export declare function generateModuleRegistry(options: GenerateOptions, moduleMetaImportPath?: string): Promise<{
9
+ outputPath: string;
10
+ moduleCount: number;
11
+ }>;
@@ -0,0 +1,353 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateModuleRegistry = generateModuleRegistry;
4
+ const promises_1 = require("node:fs/promises");
5
+ const node_path_1 = require("node:path");
6
+ const fs_utils_js_1 = require("./fs-utils.js");
7
+ const endpoint_scanner_js_1 = require("./endpoint-scanner.js");
8
+ const ui_scanner_js_1 = require("./ui-scanner.js");
9
+ const nestjs_scanner_js_1 = require("./nestjs-scanner.js");
10
+ const types_js_1 = require("./types.js");
11
+ /**
12
+ * Extract tool metadata (toolName, module, sharedWith) from a source file using regex.
13
+ */
14
+ async function extractToolMeta(absolutePath, relativePath) {
15
+ const content = await (0, fs_utils_js_1.readFileSafe)(absolutePath);
16
+ const tools = [];
17
+ // Match define*Tool blocks (brace-balanced)
18
+ const blocks = (0, fs_utils_js_1.extractBalancedBlocks)(content, /define(?:Ai)?(?:Endpoint|Ui)Tool\(\s*/);
19
+ for (const block of blocks) {
20
+ const toolNameMatch = block.match(/toolName\s*:\s*['"`]([^'"`]+)['"`]/);
21
+ if (!toolNameMatch)
22
+ continue;
23
+ const moduleMatch = block.match(/module\s*:\s*['"`]([^'"`]+)['"`]/);
24
+ // Extract sharedWith array
25
+ const sharedWithMatch = block.match(/sharedWith\s*:\s*\[([^\]]*)\]/);
26
+ let sharedWith;
27
+ if (sharedWithMatch) {
28
+ sharedWith = sharedWithMatch[1]
29
+ .match(/['"`]([^'"`]+)['"`]/g)
30
+ ?.map((s) => s.replace(/['"`]/g, ''));
31
+ }
32
+ tools.push({
33
+ toolName: toolNameMatch[1],
34
+ module: moduleMatch?.[1],
35
+ sharedWith,
36
+ sourceFile: relativePath,
37
+ });
38
+ }
39
+ return tools;
40
+ }
41
+ /**
42
+ * Generate the modules registry file.
43
+ *
44
+ * Groups all tools by their module (including sharedWith expansion),
45
+ * and generates a mapping from module → toolNames[].
46
+ */
47
+ async function generateModuleRegistry(options, moduleMetaImportPath) {
48
+ const opts = (0, types_js_1.resolveDefaults)(options);
49
+ // Scan all tool files (ai-tool.ts + ai-ui-tool.ts)
50
+ const endpointScan = await (0, endpoint_scanner_js_1.scanEndpointTools)(options);
51
+ const uiScan = await (0, ui_scanner_js_1.scanUiTools)(options);
52
+ // Extract metadata from standalone tool files
53
+ const allToolMeta = [];
54
+ for (const file of [...endpointScan.files, ...uiScan.files]) {
55
+ const meta = await extractToolMeta(file.absolutePath, file.relativePath);
56
+ allToolMeta.push(...meta);
57
+ }
58
+ // Also include NestJS @AiTool decorated controllers
59
+ if (opts.nestjsDirs.length > 0) {
60
+ const nestScan = await (0, nestjs_scanner_js_1.scanNestControllers)(opts.nestjsDirs, opts.nestjsApiPrefix);
61
+ for (const tool of nestScan.tools) {
62
+ if (tool.module) {
63
+ allToolMeta.push({
64
+ toolName: tool.name,
65
+ module: tool.module,
66
+ sharedWith: undefined,
67
+ sourceFile: tool.controllerFile,
68
+ });
69
+ }
70
+ }
71
+ }
72
+ // Group by module (with sharedWith expansion)
73
+ const moduleTools = new Map();
74
+ for (const tool of allToolMeta) {
75
+ if (!tool.module)
76
+ continue;
77
+ // Primary module
78
+ if (!moduleTools.has(tool.module)) {
79
+ moduleTools.set(tool.module, new Set());
80
+ }
81
+ moduleTools.get(tool.module).add(tool.toolName);
82
+ // Shared modules
83
+ if (tool.sharedWith) {
84
+ for (const sharedModule of tool.sharedWith) {
85
+ if (!moduleTools.has(sharedModule)) {
86
+ moduleTools.set(sharedModule, new Set());
87
+ }
88
+ moduleTools.get(sharedModule).add(tool.toolName);
89
+ }
90
+ }
91
+ }
92
+ // Sort modules and tools for deterministic output
93
+ const sortedModules = Array.from(moduleTools.keys()).sort();
94
+ // Generate toolsByModule mapping
95
+ const toolsByModuleEntries = [];
96
+ for (const module of sortedModules) {
97
+ const tools = Array.from(moduleTools.get(module)).sort();
98
+ const toolsStr = tools.map((t) => `'${t}'`).join(', ');
99
+ toolsByModuleEntries.push(` '${module}': [${toolsStr}]`);
100
+ }
101
+ // Generate module type union
102
+ const moduleTypeUnion = sortedModules.map((m) => `'${m}'`).join(' | ');
103
+ // Optional module meta integration
104
+ const metaImport = moduleMetaImportPath
105
+ ? `import { moduleMeta } from '${moduleMetaImportPath}';\n`
106
+ : '';
107
+ const metaExport = moduleMetaImportPath
108
+ ? `\nexport { moduleMeta };\n`
109
+ : '';
110
+ // Generate CLASSIFICATION_PROMPT + intent modules when module meta is available
111
+ let classificationPromptCode = '';
112
+ let intentModulesCode = '';
113
+ const metaContent = moduleMetaImportPath
114
+ ? await readModuleMeta(opts.rootDir, moduleMetaImportPath)
115
+ : null;
116
+ if (metaContent) {
117
+ classificationPromptCode = generateClassificationPrompt(sortedModules, metaContent);
118
+ intentModulesCode = generateIntentModules(sortedModules, metaContent, moduleTools);
119
+ }
120
+ const code = `/* eslint-disable */
121
+ /**
122
+ * AUTO-GENERATED FILE — do not edit manually.
123
+ * Generated by glirastes
124
+ *
125
+ * Modules: ${sortedModules.length}
126
+ * Total tool assignments: ${allToolMeta.length}
127
+ */
128
+
129
+ import { z } from 'zod';
130
+ import type { IntentModule, ModuleMeta } from 'glirastes';
131
+ import { EXECUTION_DEFAULTS } from 'glirastes';
132
+ ${metaImport}export type IntentType = ${moduleTypeUnion || 'string'};
133
+
134
+ export const IntentSchema = z.object({
135
+ intent: z.enum([${sortedModules.map((m) => `'${m}'`).join(', ')}, 'ambiguous']),
136
+ confidence: z.number().min(0).max(1),
137
+ });
138
+
139
+ export type IntentClassification = z.infer<typeof IntentSchema>;
140
+
141
+ export const toolsByModule: Record<IntentType, readonly string[]> = {
142
+ ${toolsByModuleEntries.join(',\n')}
143
+ };
144
+
145
+ export const moduleTypes: readonly IntentType[] = [${sortedModules.map((m) => `'${m}'`).join(', ')}] as const;
146
+
147
+ ${generateRelatedModules(sortedModules, moduleTools)}
148
+ /**
149
+ * Build an IntentModule by merging module meta execution config
150
+ * with EXECUTION_DEFAULTS and auto-collected toolNames.
151
+ */
152
+ export function buildIntentModule(
153
+ type: IntentType,
154
+ toolNames: readonly string[],
155
+ meta: ModuleMeta,
156
+ ): IntentModule {
157
+ const exec = { ...EXECUTION_DEFAULTS, ...meta.execution };
158
+ return {
159
+ type,
160
+ toolNames: [...toolNames],
161
+ systemPromptAddition: meta.systemPrompt,
162
+ maxSteps: exec.maxSteps,
163
+ contextWindow: exec.contextWindow,
164
+ modelTier: exec.modelTier,
165
+ meta,
166
+ };
167
+ }
168
+ ${intentModulesCode}${classificationPromptCode}${metaExport}`;
169
+ const outputPath = (0, node_path_1.join)(opts.rootDir, opts.outputDir, 'modules.generated.ts');
170
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(outputPath), { recursive: true });
171
+ await (0, promises_1.writeFile)(outputPath, code, 'utf-8');
172
+ if (!opts.quiet) {
173
+ console.log(`✓ Generated ${outputPath} (${sortedModules.length} modules)`);
174
+ }
175
+ return { outputPath, moduleCount: sortedModules.length };
176
+ }
177
+ /**
178
+ * Attempt to read and parse module metadata from a source file.
179
+ * Returns a map of module → { hint, examples, execution?, systemPrompt? } extracted via regex.
180
+ */
181
+ async function readModuleMeta(rootDir, metaImportPath) {
182
+ // Resolve import path to absolute path
183
+ const resolved = metaImportPath
184
+ .replace(/^@\//, 'src/')
185
+ .replace(/^\.\//, '');
186
+ const candidates = [
187
+ (0, node_path_1.join)(rootDir, resolved + '.ts'),
188
+ (0, node_path_1.join)(rootDir, resolved + '/index.ts'),
189
+ (0, node_path_1.join)(rootDir, resolved),
190
+ ];
191
+ let content = '';
192
+ for (const candidate of candidates) {
193
+ content = await (0, fs_utils_js_1.readFileSafe)(candidate);
194
+ if (content)
195
+ break;
196
+ }
197
+ if (!content)
198
+ return null;
199
+ const result = new Map();
200
+ // Match module entries like: task_query: { classification: { hint: '...', examples: [...] }, execution: {...}, systemPrompt: `...` }
201
+ const moduleBlocks = content.matchAll(/(\w+)\s*:\s*\{[\s\S]*?classification\s*:\s*\{([\s\S]*?)\}[\s\S]*?(?:execution|systemPrompt)/g);
202
+ for (const match of moduleBlocks) {
203
+ const moduleName = match[1];
204
+ const classBlock = match[2];
205
+ const hintMatch = classBlock.match(/hint\s*:\s*['"`]([^'"`]+)['"`]/);
206
+ const examplesMatch = classBlock.match(/examples\s*:\s*\[([\s\S]*?)\]/);
207
+ const hint = hintMatch?.[1] ?? '';
208
+ const examples = [];
209
+ if (examplesMatch) {
210
+ const exampleStrings = examplesMatch[1].matchAll(/['"`]([^'"`]+)['"`]/g);
211
+ for (const ex of exampleStrings) {
212
+ examples.push(ex[1]);
213
+ }
214
+ }
215
+ // Extract execution config from the full match
216
+ const fullBlock = match[0];
217
+ const execMatch = fullBlock.match(/execution\s*:\s*\{([\s\S]*?)\}/);
218
+ let execution;
219
+ if (execMatch) {
220
+ const execBlock = execMatch[1];
221
+ const maxStepsMatch = execBlock.match(/maxSteps\s*:\s*(\d+)/);
222
+ const contextWindowMatch = execBlock.match(/contextWindow\s*:\s*(\d+)/);
223
+ const modelTierMatch = execBlock.match(/modelTier\s*:\s*['"`]([^'"`]+)['"`]/);
224
+ if (maxStepsMatch || contextWindowMatch || modelTierMatch) {
225
+ execution = {};
226
+ if (maxStepsMatch)
227
+ execution.maxSteps = parseInt(maxStepsMatch[1], 10);
228
+ if (contextWindowMatch)
229
+ execution.contextWindow = parseInt(contextWindowMatch[1], 10);
230
+ if (modelTierMatch)
231
+ execution.modelTier = modelTierMatch[1];
232
+ }
233
+ }
234
+ // Extract systemPrompt
235
+ const sysPromptMatch = fullBlock.match(/systemPrompt\s*:\s*(?:`([\s\S]*?)`|'([^']*)'|"([^"]*)")/);
236
+ const systemPrompt = sysPromptMatch
237
+ ? (sysPromptMatch[1] ?? sysPromptMatch[2] ?? sysPromptMatch[3])?.trim()
238
+ : undefined;
239
+ if (hint || examples.length > 0) {
240
+ result.set(moduleName, { hint, examples, execution, systemPrompt });
241
+ }
242
+ }
243
+ return result.size > 0 ? result : null;
244
+ }
245
+ /**
246
+ * Generate intentModules record + getModuleForIntent / getAllModules helpers.
247
+ * Only emitted when module meta is available (has execution/systemPrompt data).
248
+ */
249
+ function generateIntentModules(modules, meta, moduleTools) {
250
+ const entries = [];
251
+ for (const mod of modules) {
252
+ const info = meta.get(mod);
253
+ const tools = moduleTools.get(mod);
254
+ if (!tools)
255
+ continue;
256
+ // Build meta object literal
257
+ const hint = info?.hint ?? '';
258
+ const examples = info?.examples ?? [];
259
+ const exStr = examples.map((e) => `'${e.replace(/'/g, "\\'")}'`).join(', ');
260
+ // Execution overrides (only non-default values)
261
+ let execStr = '';
262
+ if (info?.execution) {
263
+ const parts = [];
264
+ if (info.execution.maxSteps !== undefined)
265
+ parts.push(`maxSteps: ${info.execution.maxSteps}`);
266
+ if (info.execution.contextWindow !== undefined)
267
+ parts.push(`contextWindow: ${info.execution.contextWindow}`);
268
+ if (info.execution.modelTier !== undefined)
269
+ parts.push(`modelTier: '${info.execution.modelTier}'`);
270
+ if (parts.length > 0)
271
+ execStr = ` execution: { ${parts.join(', ')} },\n`;
272
+ }
273
+ const sysPrompt = info?.systemPrompt
274
+ ? ` systemPrompt: \`${info.systemPrompt.replace(/`/g, '\\`')}\`,\n`
275
+ : '';
276
+ entries.push(` '${mod}': buildIntentModule('${mod}', toolsByModule['${mod}'], {
277
+ classification: { hint: '${hint.replace(/'/g, "\\'")}', examples: [${exStr}] },
278
+ ${execStr}${sysPrompt} })`);
279
+ }
280
+ if (entries.length === 0)
281
+ return '';
282
+ return `
283
+ export const intentModules: Record<IntentType, IntentModule> = {
284
+ ${entries.join(',\n')}
285
+ };
286
+
287
+ export function getModuleForIntent(intent: IntentType): IntentModule | undefined {
288
+ return intentModules[intent];
289
+ }
290
+
291
+ export function getAllModules(): IntentModule[] {
292
+ return Object.values(intentModules);
293
+ }
294
+ `;
295
+ }
296
+ /**
297
+ * Generate the CLASSIFICATION_PROMPT constant from module metadata.
298
+ */
299
+ function generateClassificationPrompt(modules, meta) {
300
+ const moduleDescriptions = [];
301
+ for (const mod of modules) {
302
+ const info = meta.get(mod);
303
+ if (!info) {
304
+ moduleDescriptions.push(`- ${mod}: (no classification metadata)`);
305
+ continue;
306
+ }
307
+ const examplesStr = info.examples.length > 0
308
+ ? `\n Examples: ${info.examples.map((e) => `"${e}"`).join(', ')}`
309
+ : '';
310
+ moduleDescriptions.push(`- ${mod}: ${info.hint}${examplesStr}`);
311
+ }
312
+ const prompt = `Classify the user request into ONE of these categories:\\n\\n${moduleDescriptions.join('\\n\\n')}\\n\\n- ambiguous: Unclear, multiple areas, or small talk\\n\\nRespond with high confidence (0.8-1.0) when clear, lower (0.4-0.7) when uncertain.`;
313
+ return `\nexport const CLASSIFICATION_PROMPT = \`${prompt}\`;\n`;
314
+ }
315
+ /**
316
+ * Generate relatedModules — derived from tool overlap between modules.
317
+ * Two modules are related when they share at least one tool (via sharedWith).
318
+ */
319
+ function generateRelatedModules(modules, moduleTools) {
320
+ const entries = [];
321
+ for (const modA of modules) {
322
+ const toolsA = moduleTools.get(modA);
323
+ if (!toolsA)
324
+ continue;
325
+ const related = [];
326
+ for (const modB of modules) {
327
+ if (modA === modB)
328
+ continue;
329
+ const toolsB = moduleTools.get(modB);
330
+ if (!toolsB)
331
+ continue;
332
+ for (const tool of toolsA) {
333
+ if (toolsB.has(tool)) {
334
+ related.push(modB);
335
+ break;
336
+ }
337
+ }
338
+ }
339
+ if (related.length > 0) {
340
+ entries.push(` '${modA}': [${related.map((r) => `'${r}'`).join(', ')}]`);
341
+ }
342
+ }
343
+ if (entries.length === 0)
344
+ return '';
345
+ return `/**
346
+ * Derived from tool overlap — two modules are related when they share tools.
347
+ * Used by the pipeline router for medium-confidence tool expansion.
348
+ */
349
+ export const relatedModules: Record<string, string[]> = {
350
+ ${entries.join(',\n')}
351
+ };
352
+ `;
353
+ }
@@ -0,0 +1,43 @@
1
+ export interface DiscoveredNestTool {
2
+ name: string;
3
+ description: string;
4
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
5
+ /** Full path, e.g. /api/users/:id */
6
+ path: string;
7
+ /** From @AiModule intent */
8
+ module?: string;
9
+ /** Absolute path to the controller file */
10
+ controllerFile: string;
11
+ /** Extracted from route segments starting with ':' */
12
+ pathParams: string[];
13
+ /** Whether tool requires human approval (defaults to true for mutations) */
14
+ needsApproval?: boolean;
15
+ /** Roles that may call this tool (empty = all roles) */
16
+ allowedRoles?: string[];
17
+ }
18
+ export interface UncoveredNestController {
19
+ /** Absolute path to the controller file */
20
+ controllerFile: string;
21
+ /** Controller class name (extracted from `class XxxController`) */
22
+ controllerName: string;
23
+ /** Number of HTTP method decorators (@Get, @Post, etc.) found */
24
+ httpMethodCount: number;
25
+ }
26
+ export interface NestScanResult {
27
+ tools: DiscoveredNestTool[];
28
+ modules: Array<{
29
+ intent: string;
30
+ hint?: string;
31
+ examples?: string[];
32
+ controllerFile: string;
33
+ }>;
34
+ uncoveredControllers: UncoveredNestController[];
35
+ }
36
+ /**
37
+ * Scan NestJS controller files for `@AiTool` and `@AiModule` decorator
38
+ * metadata using static regex analysis (no runtime reflection).
39
+ *
40
+ * @param dirs Directories to scan recursively for `*.controller.ts` files.
41
+ * @param apiPrefix Optional global route prefix (e.g. `'api'`).
42
+ */
43
+ export declare function scanNestControllers(dirs: string[], apiPrefix?: string): Promise<NestScanResult>;
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scanNestControllers = scanNestControllers;
4
+ const promises_1 = require("node:fs/promises");
5
+ const node_path_1 = require("node:path");
6
+ const fs_utils_js_1 = require("./fs-utils.js");
7
+ // ---------------------------------------------------------------------------
8
+ // Helpers
9
+ // ---------------------------------------------------------------------------
10
+ function buildPath(apiPrefix, controllerPath, methodPath) {
11
+ const parts = [apiPrefix, controllerPath, methodPath].filter(Boolean);
12
+ const joined = parts.join('/').replace(/\/+/g, '/').replace(/^\/|\/$/g, '');
13
+ return '/' + joined;
14
+ }
15
+ function extractPathParams(path) {
16
+ return (path.match(/:(\w+)/g) || []).map((p) => p.slice(1));
17
+ }
18
+ /**
19
+ * Recursively find all `*.controller.ts` files in a directory,
20
+ * skipping `node_modules` and `.git`.
21
+ */
22
+ async function findControllerFiles(dir) {
23
+ const results = [];
24
+ async function walk(currentDir) {
25
+ let entries;
26
+ try {
27
+ entries = await (0, promises_1.readdir)(currentDir, { withFileTypes: true });
28
+ }
29
+ catch {
30
+ return;
31
+ }
32
+ for (const entry of entries) {
33
+ const fullPath = (0, node_path_1.join)(currentDir, entry.name);
34
+ if (entry.isDirectory()) {
35
+ if (entry.name === 'node_modules' || entry.name === '.git')
36
+ continue;
37
+ await walk(fullPath);
38
+ }
39
+ else if (entry.name.endsWith('.controller.ts')) {
40
+ results.push(fullPath);
41
+ }
42
+ }
43
+ }
44
+ await walk(dir);
45
+ return results.sort();
46
+ }
47
+ const HTTP_DECORATOR_RE = /@(Get|Post|Put|Patch|Delete)\(\s*(?:['"]([^'"]*)['"]\s*)?\)/g;
48
+ function findHttpDecorators(content) {
49
+ const matches = [];
50
+ const re = new RegExp(HTTP_DECORATOR_RE.source, 'g');
51
+ let m;
52
+ while ((m = re.exec(content)) !== null) {
53
+ matches.push({
54
+ method: m[1].toUpperCase(),
55
+ subRoute: m[2] ?? '',
56
+ index: m.index,
57
+ });
58
+ }
59
+ return matches;
60
+ }
61
+ /**
62
+ * For a given character index in `content`, find the nearest preceding HTTP
63
+ * method decorator.
64
+ */
65
+ function nearestHttpDecorator(decorators, position) {
66
+ let best;
67
+ for (const d of decorators) {
68
+ if (d.index < position) {
69
+ if (!best || d.index > best.index) {
70
+ best = d;
71
+ }
72
+ }
73
+ }
74
+ return best;
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Parsing helpers
78
+ // ---------------------------------------------------------------------------
79
+ function parseControllerPath(content) {
80
+ const m = content.match(/@Controller\(\s*['"]([^'"]*)['"]\s*\)/);
81
+ return m?.[1] ?? '';
82
+ }
83
+ function parseAiModule(content) {
84
+ const blocks = (0, fs_utils_js_1.extractBalancedBlocks)(content, /@AiModule\(\s*/);
85
+ if (blocks.length === 0)
86
+ return undefined;
87
+ const block = blocks[0];
88
+ const intentMatch = block.match(/intent:\s*['"]([^'"]+)['"]/);
89
+ if (!intentMatch)
90
+ return undefined;
91
+ const hint = block.match(/hint:\s*['"]([^'"]+)['"]/)?.[1];
92
+ let examples;
93
+ const examplesMatch = block.match(/examples:\s*\[([^\]]*)\]/);
94
+ if (examplesMatch) {
95
+ const inner = examplesMatch[1];
96
+ examples = [];
97
+ const strRe = /['"]([^'"]+)['"]/g;
98
+ let sm;
99
+ while ((sm = strRe.exec(inner)) !== null) {
100
+ examples.push(sm[1]);
101
+ }
102
+ }
103
+ return { intent: intentMatch[1], hint, examples };
104
+ }
105
+ function parseAiTools(content) {
106
+ const tools = [];
107
+ // We need to know the source index of each @AiTool match.
108
+ // extractBalancedBlocks doesn't expose offsets, so we locate them manually.
109
+ const aiToolRe = /@AiTool\(\s*/g;
110
+ let prefixMatch;
111
+ while ((prefixMatch = aiToolRe.exec(content)) !== null) {
112
+ const searchStart = prefixMatch.index + prefixMatch[0].length;
113
+ const braceStart = content.indexOf('{', searchStart);
114
+ if (braceStart === -1)
115
+ continue;
116
+ // Balanced-brace walk
117
+ let depth = 0;
118
+ let i = braceStart;
119
+ for (; i < content.length; i++) {
120
+ if (content[i] === '{')
121
+ depth++;
122
+ else if (content[i] === '}')
123
+ depth--;
124
+ if (depth === 0)
125
+ break;
126
+ }
127
+ if (depth !== 0)
128
+ continue;
129
+ const inner = content.slice(braceStart + 1, i);
130
+ const nameMatch = inner.match(/name:\s*['"`]([^'"`]+)['"`]/);
131
+ const descMatch = inner.match(/description:\s*['"]([^'"]+)['"]/)
132
+ || inner.match(/description:\s*`([\s\S]*?)`/);
133
+ if (nameMatch) {
134
+ const description = (descMatch?.[1] ?? descMatch?.[2] ?? '').replace(/\s+/g, ' ').trim();
135
+ // Extract needsApproval (boolean literal)
136
+ const approvalMatch = inner.match(/needsApproval:\s*(true|false)/);
137
+ const needsApproval = approvalMatch ? approvalMatch[1] === 'true' : undefined;
138
+ // Extract allowedRoles (string array)
139
+ let allowedRoles;
140
+ const rolesMatch = inner.match(/allowedRoles:\s*\[([^\]]*)\]/);
141
+ if (rolesMatch) {
142
+ allowedRoles = [];
143
+ const roleRe = /['"`]([^'"`]+)['"`]/g;
144
+ let rm;
145
+ while ((rm = roleRe.exec(rolesMatch[1])) !== null) {
146
+ allowedRoles.push(rm[1]);
147
+ }
148
+ }
149
+ tools.push({
150
+ name: nameMatch[1],
151
+ description,
152
+ needsApproval,
153
+ allowedRoles,
154
+ sourceIndex: prefixMatch.index,
155
+ });
156
+ }
157
+ }
158
+ return tools;
159
+ }
160
+ // ---------------------------------------------------------------------------
161
+ // Main scanner
162
+ // ---------------------------------------------------------------------------
163
+ /**
164
+ * Scan NestJS controller files for `@AiTool` and `@AiModule` decorator
165
+ * metadata using static regex analysis (no runtime reflection).
166
+ *
167
+ * @param dirs Directories to scan recursively for `*.controller.ts` files.
168
+ * @param apiPrefix Optional global route prefix (e.g. `'api'`).
169
+ */
170
+ async function scanNestControllers(dirs, apiPrefix = '') {
171
+ const result = { tools: [], modules: [], uncoveredControllers: [] };
172
+ // 1. Collect all controller files from every directory
173
+ const allFiles = [];
174
+ for (const dir of dirs) {
175
+ const files = await findControllerFiles(dir);
176
+ allFiles.push(...files);
177
+ }
178
+ // 2. Parse each controller
179
+ for (const filePath of allFiles) {
180
+ let content;
181
+ try {
182
+ content = await (0, promises_1.readFile)(filePath, 'utf-8');
183
+ }
184
+ catch {
185
+ continue;
186
+ }
187
+ // Check ignore markers — only skip file when @ai-ignore appears before
188
+ // the first import/class (file-level ignore). Method-level // @ai-ignore
189
+ // comments are fine — methods without @AiTool are already skipped.
190
+ const preamble = content.slice(0, content.search(/^(?:import|@Controller|@Module|export\s+class)\b/m) >>> 0 || 200);
191
+ if (preamble.includes('@ai-ignore'))
192
+ continue;
193
+ if (await (0, fs_utils_js_1.isDirAiIgnored)((0, node_path_1.dirname)(filePath)))
194
+ continue;
195
+ // Controller base path
196
+ const controllerPath = parseControllerPath(content);
197
+ // Module metadata
198
+ const moduleMeta = parseAiModule(content);
199
+ if (moduleMeta) {
200
+ result.modules.push({
201
+ ...moduleMeta,
202
+ controllerFile: filePath,
203
+ });
204
+ }
205
+ // Tool metadata
206
+ const rawTools = parseAiTools(content);
207
+ if (rawTools.length === 0) {
208
+ // Check if this controller has HTTP endpoints that should have @AiTool
209
+ const httpDecorators = findHttpDecorators(content);
210
+ if (httpDecorators.length > 0) {
211
+ const classMatch = content.match(/class\s+(\w+)/);
212
+ result.uncoveredControllers.push({
213
+ controllerFile: filePath,
214
+ controllerName: classMatch?.[1] ?? 'UnknownController',
215
+ httpMethodCount: httpDecorators.length,
216
+ });
217
+ }
218
+ continue;
219
+ }
220
+ const httpDecorators = findHttpDecorators(content);
221
+ for (const tool of rawTools) {
222
+ const httpDec = nearestHttpDecorator(httpDecorators, tool.sourceIndex);
223
+ const method = httpDec?.method ?? 'GET';
224
+ const subRoute = httpDec?.subRoute ?? '';
225
+ const fullPath = buildPath(apiPrefix, controllerPath, subRoute);
226
+ const pathParams = extractPathParams(fullPath);
227
+ result.tools.push({
228
+ name: tool.name,
229
+ description: tool.description,
230
+ method,
231
+ path: fullPath,
232
+ module: moduleMeta?.intent,
233
+ controllerFile: filePath,
234
+ pathParams,
235
+ needsApproval: tool.needsApproval,
236
+ allowedRoles: tool.allowedRoles,
237
+ });
238
+ }
239
+ }
240
+ return result;
241
+ }
@@ -0,0 +1,23 @@
1
+ import type { AgentAuthStrategy } from '../types.js';
2
+ export type OutputFormat = 'skill' | 'mcp' | 'both';
3
+ export interface RecommendationInput {
4
+ auth: AgentAuthStrategy;
5
+ hasStreaming?: boolean;
6
+ hasWebSocket?: boolean;
7
+ multiAgentAccess?: boolean;
8
+ toolCount?: number;
9
+ }
10
+ /**
11
+ * Recommend the best output format (skill, mcp, or both) based on
12
+ * the project's auth strategy, transport needs, and tool count.
13
+ *
14
+ * - OAuth2 → MCP (needs persistent token management)
15
+ * - Streaming/WebSocket → MCP (needs persistent connection)
16
+ * - Multi-agent → MCP (shared server benefits)
17
+ * - Many tools (>20) → both (MCP for power users, skill for simple use)
18
+ * - Default → skill (simpler, no server process needed)
19
+ */
20
+ export declare function recommendOutputFormat(input: RecommendationInput): {
21
+ format: OutputFormat;
22
+ reason: string;
23
+ };