memory-journal-mcp 7.7.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (531) hide show
  1. package/README.md +126 -56
  2. package/dist/chunk-6OHRCNYW.js +3231 -0
  3. package/dist/chunk-JFMITANR.js +5168 -0
  4. package/dist/{chunk-QCQPAF4I.js → chunk-MWNLAEHR.js} +301 -4321
  5. package/dist/{chunk-ARLYSFSI.js → chunk-UHSO65A4.js} +4242 -6092
  6. package/dist/cli.js +21 -3
  7. package/dist/index.d.ts +16 -13
  8. package/dist/index.js +4 -2
  9. package/dist/resources-IJVKDFGS.js +2 -0
  10. package/dist/tools-44DGXE3V.js +2 -0
  11. package/dist/worker-script.js +201 -20
  12. package/package.json +7 -4
  13. package/skills/README.md +62 -25
  14. package/skills/adversarial-performance/SKILL.md +139 -0
  15. package/skills/adversarial-performance/references/audit-categories.md +462 -0
  16. package/skills/adversarial-performance/references/copilot-performance-prompts.md +44 -0
  17. package/skills/adversarial-performance/references/copilot-usage.md +16 -0
  18. package/skills/adversarial-performance/references/feedback-loop.md +177 -0
  19. package/skills/adversarial-performance/references/multi-pass-performance-protocol.md +398 -0
  20. package/skills/adversarial-planner/SKILL.md +23 -54
  21. package/skills/adversarial-planner/references/copilot-integration.md +25 -40
  22. package/skills/adversarial-planner/references/copilot-usage.md +16 -0
  23. package/skills/adversarial-planner/references/multi-pass-protocol.md +4 -0
  24. package/skills/adversarial-security/SKILL.md +149 -0
  25. package/skills/adversarial-security/references/adversarial-base-protocol.md +44 -0
  26. package/skills/adversarial-security/references/audit-categories.md +723 -0
  27. package/skills/adversarial-security/references/copilot-security-prompts.md +142 -0
  28. package/skills/adversarial-security/references/copilot-usage.md +16 -0
  29. package/skills/adversarial-security/references/feedback-loop.md +206 -0
  30. package/skills/adversarial-security/references/journal-opt-out.md +7 -0
  31. package/skills/adversarial-security/references/multi-pass-security-protocol.md +403 -0
  32. package/skills/adversarial-skill-audit/SKILL.md +118 -0
  33. package/skills/adversarial-skill-audit/references/audit-categories.md +308 -0
  34. package/skills/adversarial-skill-audit/references/copilot-skill-prompts.md +68 -0
  35. package/skills/adversarial-skill-audit/references/copilot-usage.md +16 -0
  36. package/skills/adversarial-skill-audit/references/feedback-loop.md +155 -0
  37. package/skills/adversarial-skill-audit/references/multi-pass-skill-protocol.md +367 -0
  38. package/skills/adversarial-skill-audit/scripts/check-skills.ps1 +48 -0
  39. package/skills/adversarial-skill-audit/scripts/run-copilot.ps1 +52 -0
  40. package/skills/adversarial-workflow-audit/SKILL.md +82 -0
  41. package/skills/adversarial-workflow-audit/references/audit-categories.md +28 -0
  42. package/skills/adversarial-workflow-audit/references/copilot-usage.md +16 -0
  43. package/skills/adversarial-workflow-audit/scripts/check-workflows.ps1 +24 -0
  44. package/skills/agents-sdk/SKILL.md +220 -0
  45. package/skills/agents-sdk/references/callable.md +92 -0
  46. package/skills/agents-sdk/references/codemode.md +209 -0
  47. package/skills/agents-sdk/references/email.md +144 -0
  48. package/skills/agents-sdk/references/mcp/SKILL.md +65 -0
  49. package/skills/agents-sdk/references/mcp/code-mode-reference.md +245 -0
  50. package/skills/agents-sdk/references/mcp/oauth-reference.md +359 -0
  51. package/skills/agents-sdk/references/mcp/references/architecture-reference.md +208 -0
  52. package/skills/agents-sdk/references/mcp/references/cloudflare-quickstart.md +156 -0
  53. package/skills/agents-sdk/references/mcp/references/error-handling.md +343 -0
  54. package/skills/agents-sdk/references/mcp/references/http-security.md +164 -0
  55. package/skills/agents-sdk/references/mcp/references/implementation-guide.md +507 -0
  56. package/skills/agents-sdk/references/mcp/references/testing-reference.md +171 -0
  57. package/skills/agents-sdk/references/mcp.md +157 -0
  58. package/skills/agents-sdk/references/state-scheduling.md +164 -0
  59. package/skills/agents-sdk/references/streaming-chat.md +168 -0
  60. package/skills/agents-sdk/references/workflows.md +136 -0
  61. package/skills/auth-identity/SKILL.md +48 -0
  62. package/skills/autonomous-dev/SKILL.md +46 -23
  63. package/skills/autonomous-dev/references/workflow_orchestration.md +22 -0
  64. package/skills/aws/SKILL.md +39 -0
  65. package/skills/azure/SKILL.md +38 -0
  66. package/skills/bin/sync.js +7 -1
  67. package/skills/biome/SKILL.md +59 -0
  68. package/skills/bun/SKILL.md +8 -2
  69. package/skills/cloudflare/SKILL.md +37 -0
  70. package/skills/cloudflare/references/agents-sdk/README.md +95 -0
  71. package/skills/cloudflare/references/agents-sdk/api.md +195 -0
  72. package/skills/cloudflare/references/agents-sdk/configuration.md +178 -0
  73. package/skills/cloudflare/references/agents-sdk/gotchas.md +173 -0
  74. package/skills/cloudflare/references/agents-sdk/patterns.md +215 -0
  75. package/skills/cloudflare/references/ai-gateway/README.md +176 -0
  76. package/skills/cloudflare/references/ai-gateway/configuration.md +117 -0
  77. package/skills/cloudflare/references/ai-gateway/dynamic-routing.md +88 -0
  78. package/skills/cloudflare/references/ai-gateway/features.md +96 -0
  79. package/skills/cloudflare/references/ai-gateway/sdk-integration.md +110 -0
  80. package/skills/cloudflare/references/ai-gateway/troubleshooting.md +90 -0
  81. package/skills/cloudflare/references/ai-search/README.md +145 -0
  82. package/skills/cloudflare/references/ai-search/api.md +87 -0
  83. package/skills/cloudflare/references/ai-search/configuration.md +91 -0
  84. package/skills/cloudflare/references/ai-search/gotchas.md +92 -0
  85. package/skills/cloudflare/references/ai-search/patterns.md +87 -0
  86. package/skills/cloudflare/references/analytics-engine/README.md +96 -0
  87. package/skills/cloudflare/references/analytics-engine/api.md +112 -0
  88. package/skills/cloudflare/references/analytics-engine/configuration.md +107 -0
  89. package/skills/cloudflare/references/analytics-engine/gotchas.md +87 -0
  90. package/skills/cloudflare/references/analytics-engine/patterns.md +83 -0
  91. package/skills/cloudflare/references/api/README.md +66 -0
  92. package/skills/cloudflare/references/api/api.md +205 -0
  93. package/skills/cloudflare/references/api/configuration.md +158 -0
  94. package/skills/cloudflare/references/api/gotchas.md +231 -0
  95. package/skills/cloudflare/references/api/patterns.md +208 -0
  96. package/skills/cloudflare/references/api-shield/README.md +44 -0
  97. package/skills/cloudflare/references/api-shield/api.md +153 -0
  98. package/skills/cloudflare/references/api-shield/configuration.md +210 -0
  99. package/skills/cloudflare/references/api-shield/gotchas.md +132 -0
  100. package/skills/cloudflare/references/api-shield/patterns.md +185 -0
  101. package/skills/cloudflare/references/argo-smart-routing/README.md +96 -0
  102. package/skills/cloudflare/references/argo-smart-routing/api.md +253 -0
  103. package/skills/cloudflare/references/argo-smart-routing/configuration.md +205 -0
  104. package/skills/cloudflare/references/argo-smart-routing/gotchas.md +115 -0
  105. package/skills/cloudflare/references/argo-smart-routing/patterns.md +107 -0
  106. package/skills/cloudflare/references/bindings/README.md +127 -0
  107. package/skills/cloudflare/references/bindings/api.md +214 -0
  108. package/skills/cloudflare/references/bindings/configuration.md +200 -0
  109. package/skills/cloudflare/references/bindings/gotchas.md +210 -0
  110. package/skills/cloudflare/references/bindings/patterns.md +205 -0
  111. package/skills/cloudflare/references/bot-management/README.md +95 -0
  112. package/skills/cloudflare/references/bot-management/api.md +175 -0
  113. package/skills/cloudflare/references/bot-management/configuration.md +175 -0
  114. package/skills/cloudflare/references/bot-management/gotchas.md +116 -0
  115. package/skills/cloudflare/references/bot-management/patterns.md +181 -0
  116. package/skills/cloudflare/references/browser-rendering/README.md +84 -0
  117. package/skills/cloudflare/references/browser-rendering/api.md +108 -0
  118. package/skills/cloudflare/references/browser-rendering/configuration.md +78 -0
  119. package/skills/cloudflare/references/browser-rendering/gotchas.md +91 -0
  120. package/skills/cloudflare/references/browser-rendering/patterns.md +93 -0
  121. package/skills/cloudflare/references/c3/README.md +111 -0
  122. package/skills/cloudflare/references/c3/api.md +71 -0
  123. package/skills/cloudflare/references/c3/configuration.md +85 -0
  124. package/skills/cloudflare/references/c3/gotchas.md +97 -0
  125. package/skills/cloudflare/references/c3/patterns.md +84 -0
  126. package/skills/cloudflare/references/cache-reserve/README.md +150 -0
  127. package/skills/cloudflare/references/cache-reserve/api.md +184 -0
  128. package/skills/cloudflare/references/cache-reserve/configuration.md +170 -0
  129. package/skills/cloudflare/references/cache-reserve/gotchas.md +136 -0
  130. package/skills/cloudflare/references/cache-reserve/patterns.md +197 -0
  131. package/skills/cloudflare/references/containers/README.md +87 -0
  132. package/skills/cloudflare/references/containers/api.md +197 -0
  133. package/skills/cloudflare/references/containers/configuration.md +191 -0
  134. package/skills/cloudflare/references/containers/gotchas.md +182 -0
  135. package/skills/cloudflare/references/containers/patterns.md +204 -0
  136. package/skills/cloudflare/references/cron-triggers/README.md +101 -0
  137. package/skills/cloudflare/references/cron-triggers/api.md +224 -0
  138. package/skills/cloudflare/references/cron-triggers/configuration.md +190 -0
  139. package/skills/cloudflare/references/cron-triggers/gotchas.md +207 -0
  140. package/skills/cloudflare/references/cron-triggers/patterns.md +274 -0
  141. package/skills/cloudflare/references/d1/README.md +137 -0
  142. package/skills/cloudflare/references/d1/api.md +213 -0
  143. package/skills/cloudflare/references/d1/configuration.md +198 -0
  144. package/skills/cloudflare/references/d1/gotchas.md +98 -0
  145. package/skills/cloudflare/references/d1/patterns.md +240 -0
  146. package/skills/cloudflare/references/ddos/README.md +42 -0
  147. package/skills/cloudflare/references/ddos/api.md +158 -0
  148. package/skills/cloudflare/references/ddos/configuration.md +94 -0
  149. package/skills/cloudflare/references/ddos/gotchas.md +114 -0
  150. package/skills/cloudflare/references/ddos/patterns.md +220 -0
  151. package/skills/cloudflare/references/decision-trees.md +95 -0
  152. package/skills/cloudflare/references/do-storage/README.md +79 -0
  153. package/skills/cloudflare/references/do-storage/api.md +107 -0
  154. package/skills/cloudflare/references/do-storage/configuration.md +114 -0
  155. package/skills/cloudflare/references/do-storage/gotchas.md +153 -0
  156. package/skills/cloudflare/references/do-storage/patterns.md +210 -0
  157. package/skills/cloudflare/references/do-storage/testing.md +186 -0
  158. package/skills/cloudflare/references/durable-objects/README.md +194 -0
  159. package/skills/cloudflare/references/durable-objects/api.md +205 -0
  160. package/skills/cloudflare/references/durable-objects/configuration.md +160 -0
  161. package/skills/cloudflare/references/durable-objects/gotchas.md +200 -0
  162. package/skills/cloudflare/references/durable-objects/patterns.md +205 -0
  163. package/skills/cloudflare/references/email-routing/README.md +89 -0
  164. package/skills/cloudflare/references/email-routing/api.md +192 -0
  165. package/skills/cloudflare/references/email-routing/configuration.md +187 -0
  166. package/skills/cloudflare/references/email-routing/gotchas.md +203 -0
  167. package/skills/cloudflare/references/email-routing/patterns.md +241 -0
  168. package/skills/cloudflare/references/email-workers/README.md +153 -0
  169. package/skills/cloudflare/references/email-workers/api.md +227 -0
  170. package/skills/cloudflare/references/email-workers/configuration.md +115 -0
  171. package/skills/cloudflare/references/email-workers/gotchas.md +133 -0
  172. package/skills/cloudflare/references/email-workers/patterns.md +108 -0
  173. package/skills/cloudflare/references/graphql-api/README.md +147 -0
  174. package/skills/cloudflare/references/graphql-api/api.md +175 -0
  175. package/skills/cloudflare/references/graphql-api/configuration.md +151 -0
  176. package/skills/cloudflare/references/graphql-api/gotchas.md +111 -0
  177. package/skills/cloudflare/references/graphql-api/patterns.md +276 -0
  178. package/skills/cloudflare/references/hyperdrive/README.md +84 -0
  179. package/skills/cloudflare/references/hyperdrive/api.md +149 -0
  180. package/skills/cloudflare/references/hyperdrive/configuration.md +166 -0
  181. package/skills/cloudflare/references/hyperdrive/gotchas.md +77 -0
  182. package/skills/cloudflare/references/hyperdrive/patterns.md +203 -0
  183. package/skills/cloudflare/references/images/README.md +65 -0
  184. package/skills/cloudflare/references/images/api.md +101 -0
  185. package/skills/cloudflare/references/images/configuration.md +206 -0
  186. package/skills/cloudflare/references/images/gotchas.md +106 -0
  187. package/skills/cloudflare/references/images/patterns.md +126 -0
  188. package/skills/cloudflare/references/kv/README.md +90 -0
  189. package/skills/cloudflare/references/kv/api.md +163 -0
  190. package/skills/cloudflare/references/kv/configuration.md +148 -0
  191. package/skills/cloudflare/references/kv/gotchas.md +133 -0
  192. package/skills/cloudflare/references/kv/patterns.md +195 -0
  193. package/skills/cloudflare/references/miniflare/README.md +113 -0
  194. package/skills/cloudflare/references/miniflare/api.md +204 -0
  195. package/skills/cloudflare/references/miniflare/configuration.md +174 -0
  196. package/skills/cloudflare/references/miniflare/gotchas.md +179 -0
  197. package/skills/cloudflare/references/miniflare/patterns.md +187 -0
  198. package/skills/cloudflare/references/network-interconnect/README.md +104 -0
  199. package/skills/cloudflare/references/network-interconnect/api.md +220 -0
  200. package/skills/cloudflare/references/network-interconnect/configuration.md +123 -0
  201. package/skills/cloudflare/references/network-interconnect/gotchas.md +175 -0
  202. package/skills/cloudflare/references/network-interconnect/patterns.md +174 -0
  203. package/skills/cloudflare/references/observability/README.md +93 -0
  204. package/skills/cloudflare/references/observability/api.md +168 -0
  205. package/skills/cloudflare/references/observability/configuration.md +178 -0
  206. package/skills/cloudflare/references/observability/gotchas.md +125 -0
  207. package/skills/cloudflare/references/observability/patterns.md +105 -0
  208. package/skills/cloudflare/references/pages/README.md +92 -0
  209. package/skills/cloudflare/references/pages/api.md +205 -0
  210. package/skills/cloudflare/references/pages/configuration.md +216 -0
  211. package/skills/cloudflare/references/pages/gotchas.md +218 -0
  212. package/skills/cloudflare/references/pages/patterns.md +215 -0
  213. package/skills/cloudflare/references/pages-functions/README.md +104 -0
  214. package/skills/cloudflare/references/pages-functions/api.md +159 -0
  215. package/skills/cloudflare/references/pages-functions/configuration.md +130 -0
  216. package/skills/cloudflare/references/pages-functions/gotchas.md +102 -0
  217. package/skills/cloudflare/references/pages-functions/patterns.md +148 -0
  218. package/skills/cloudflare/references/pipelines/README.md +109 -0
  219. package/skills/cloudflare/references/pipelines/api.md +214 -0
  220. package/skills/cloudflare/references/pipelines/configuration.md +98 -0
  221. package/skills/cloudflare/references/pipelines/gotchas.md +84 -0
  222. package/skills/cloudflare/references/pipelines/patterns.md +87 -0
  223. package/skills/cloudflare/references/product-index.md +112 -0
  224. package/skills/cloudflare/references/pulumi/README.md +113 -0
  225. package/skills/cloudflare/references/pulumi/api.md +230 -0
  226. package/skills/cloudflare/references/pulumi/configuration.md +213 -0
  227. package/skills/cloudflare/references/pulumi/gotchas.md +205 -0
  228. package/skills/cloudflare/references/pulumi/patterns.md +260 -0
  229. package/skills/cloudflare/references/queues/README.md +99 -0
  230. package/skills/cloudflare/references/queues/api.md +211 -0
  231. package/skills/cloudflare/references/queues/configuration.md +151 -0
  232. package/skills/cloudflare/references/queues/gotchas.md +210 -0
  233. package/skills/cloudflare/references/queues/patterns.md +220 -0
  234. package/skills/cloudflare/references/r2/README.md +97 -0
  235. package/skills/cloudflare/references/r2/api.md +235 -0
  236. package/skills/cloudflare/references/r2/configuration.md +176 -0
  237. package/skills/cloudflare/references/r2/gotchas.md +190 -0
  238. package/skills/cloudflare/references/r2/patterns.md +203 -0
  239. package/skills/cloudflare/references/r2-data-catalog/README.md +157 -0
  240. package/skills/cloudflare/references/r2-data-catalog/api.md +199 -0
  241. package/skills/cloudflare/references/r2-data-catalog/configuration.md +205 -0
  242. package/skills/cloudflare/references/r2-data-catalog/gotchas.md +170 -0
  243. package/skills/cloudflare/references/r2-data-catalog/patterns.md +191 -0
  244. package/skills/cloudflare/references/r2-sql/README.md +138 -0
  245. package/skills/cloudflare/references/r2-sql/SKILL.md.backup +512 -0
  246. package/skills/cloudflare/references/r2-sql/api.md +159 -0
  247. package/skills/cloudflare/references/r2-sql/configuration.md +152 -0
  248. package/skills/cloudflare/references/r2-sql/gotchas.md +228 -0
  249. package/skills/cloudflare/references/r2-sql/patterns.md +230 -0
  250. package/skills/cloudflare/references/realtime-sfu/README.md +66 -0
  251. package/skills/cloudflare/references/realtime-sfu/api.md +164 -0
  252. package/skills/cloudflare/references/realtime-sfu/configuration.md +141 -0
  253. package/skills/cloudflare/references/realtime-sfu/gotchas.md +138 -0
  254. package/skills/cloudflare/references/realtime-sfu/patterns.md +187 -0
  255. package/skills/cloudflare/references/realtimekit/README.md +118 -0
  256. package/skills/cloudflare/references/realtimekit/api.md +234 -0
  257. package/skills/cloudflare/references/realtimekit/configuration.md +226 -0
  258. package/skills/cloudflare/references/realtimekit/gotchas.md +206 -0
  259. package/skills/cloudflare/references/realtimekit/patterns.md +240 -0
  260. package/skills/cloudflare/references/sandbox/README.md +104 -0
  261. package/skills/cloudflare/references/sandbox/api.md +200 -0
  262. package/skills/cloudflare/references/sandbox/configuration.md +154 -0
  263. package/skills/cloudflare/references/sandbox/gotchas.md +201 -0
  264. package/skills/cloudflare/references/sandbox/patterns.md +195 -0
  265. package/skills/cloudflare/references/secrets-store/README.md +77 -0
  266. package/skills/cloudflare/references/secrets-store/api.md +199 -0
  267. package/skills/cloudflare/references/secrets-store/configuration.md +187 -0
  268. package/skills/cloudflare/references/secrets-store/gotchas.md +97 -0
  269. package/skills/cloudflare/references/secrets-store/patterns.md +218 -0
  270. package/skills/cloudflare/references/smart-placement/README.md +143 -0
  271. package/skills/cloudflare/references/smart-placement/api.md +192 -0
  272. package/skills/cloudflare/references/smart-placement/configuration.md +202 -0
  273. package/skills/cloudflare/references/smart-placement/gotchas.md +180 -0
  274. package/skills/cloudflare/references/smart-placement/patterns.md +190 -0
  275. package/skills/cloudflare/references/snippets/README.md +74 -0
  276. package/skills/cloudflare/references/snippets/api.md +214 -0
  277. package/skills/cloudflare/references/snippets/configuration.md +239 -0
  278. package/skills/cloudflare/references/snippets/gotchas.md +104 -0
  279. package/skills/cloudflare/references/snippets/patterns.md +135 -0
  280. package/skills/cloudflare/references/spectrum/README.md +52 -0
  281. package/skills/cloudflare/references/spectrum/api.md +184 -0
  282. package/skills/cloudflare/references/spectrum/configuration.md +203 -0
  283. package/skills/cloudflare/references/spectrum/gotchas.md +155 -0
  284. package/skills/cloudflare/references/spectrum/patterns.md +206 -0
  285. package/skills/cloudflare/references/static-assets/README.md +65 -0
  286. package/skills/cloudflare/references/static-assets/api.md +201 -0
  287. package/skills/cloudflare/references/static-assets/configuration.md +186 -0
  288. package/skills/cloudflare/references/static-assets/gotchas.md +164 -0
  289. package/skills/cloudflare/references/static-assets/patterns.md +189 -0
  290. package/skills/cloudflare/references/stream/README.md +123 -0
  291. package/skills/cloudflare/references/stream/api-live.md +202 -0
  292. package/skills/cloudflare/references/stream/api.md +206 -0
  293. package/skills/cloudflare/references/stream/configuration.md +151 -0
  294. package/skills/cloudflare/references/stream/gotchas.md +139 -0
  295. package/skills/cloudflare/references/stream/patterns.md +217 -0
  296. package/skills/cloudflare/references/tail-workers/README.md +92 -0
  297. package/skills/cloudflare/references/tail-workers/api.md +203 -0
  298. package/skills/cloudflare/references/tail-workers/configuration.md +178 -0
  299. package/skills/cloudflare/references/tail-workers/gotchas.md +206 -0
  300. package/skills/cloudflare/references/tail-workers/patterns.md +190 -0
  301. package/skills/cloudflare/references/terraform/README.md +100 -0
  302. package/skills/cloudflare/references/terraform/api.md +178 -0
  303. package/skills/cloudflare/references/terraform/configuration.md +197 -0
  304. package/skills/cloudflare/references/terraform/gotchas.md +150 -0
  305. package/skills/cloudflare/references/terraform/patterns.md +174 -0
  306. package/skills/cloudflare/references/tunnel/README.md +137 -0
  307. package/skills/cloudflare/references/tunnel/api.md +205 -0
  308. package/skills/cloudflare/references/tunnel/configuration.md +163 -0
  309. package/skills/cloudflare/references/tunnel/gotchas.md +159 -0
  310. package/skills/cloudflare/references/tunnel/networking.md +174 -0
  311. package/skills/cloudflare/references/tunnel/patterns.md +199 -0
  312. package/skills/cloudflare/references/turn/README.md +86 -0
  313. package/skills/cloudflare/references/turn/api.md +236 -0
  314. package/skills/cloudflare/references/turn/configuration.md +181 -0
  315. package/skills/cloudflare/references/turn/gotchas.md +236 -0
  316. package/skills/cloudflare/references/turn/patterns.md +228 -0
  317. package/skills/cloudflare/references/turnstile/README.md +102 -0
  318. package/skills/cloudflare/references/turnstile/api.md +253 -0
  319. package/skills/cloudflare/references/turnstile/configuration.md +242 -0
  320. package/skills/cloudflare/references/turnstile/gotchas.md +253 -0
  321. package/skills/cloudflare/references/turnstile/patterns.md +195 -0
  322. package/skills/cloudflare/references/vectorize/README.md +133 -0
  323. package/skills/cloudflare/references/vectorize/api.md +89 -0
  324. package/skills/cloudflare/references/vectorize/configuration.md +91 -0
  325. package/skills/cloudflare/references/vectorize/gotchas.md +83 -0
  326. package/skills/cloudflare/references/vectorize/patterns.md +92 -0
  327. package/skills/cloudflare/references/waf/README.md +125 -0
  328. package/skills/cloudflare/references/waf/api.md +203 -0
  329. package/skills/cloudflare/references/waf/configuration.md +215 -0
  330. package/skills/cloudflare/references/waf/gotchas.md +208 -0
  331. package/skills/cloudflare/references/waf/patterns.md +236 -0
  332. package/skills/cloudflare/references/web-analytics/README.md +149 -0
  333. package/skills/cloudflare/references/web-analytics/configuration.md +81 -0
  334. package/skills/cloudflare/references/web-analytics/gotchas.md +86 -0
  335. package/skills/cloudflare/references/web-analytics/integration.md +63 -0
  336. package/skills/cloudflare/references/web-analytics/patterns.md +98 -0
  337. package/skills/cloudflare/references/workerd/README.md +85 -0
  338. package/skills/cloudflare/references/workerd/api.md +219 -0
  339. package/skills/cloudflare/references/workerd/configuration.md +200 -0
  340. package/skills/cloudflare/references/workerd/gotchas.md +151 -0
  341. package/skills/cloudflare/references/workerd/patterns.md +205 -0
  342. package/skills/cloudflare/references/workers/README.md +110 -0
  343. package/skills/cloudflare/references/workers/api.md +197 -0
  344. package/skills/cloudflare/references/workers/configuration.md +184 -0
  345. package/skills/cloudflare/references/workers/frameworks.md +200 -0
  346. package/skills/cloudflare/references/workers/gotchas.md +145 -0
  347. package/skills/cloudflare/references/workers/patterns.md +220 -0
  348. package/skills/cloudflare/references/workers-ai/README.md +206 -0
  349. package/skills/cloudflare/references/workers-ai/api.md +115 -0
  350. package/skills/cloudflare/references/workers-ai/configuration.md +98 -0
  351. package/skills/cloudflare/references/workers-ai/gotchas.md +130 -0
  352. package/skills/cloudflare/references/workers-ai/patterns.md +122 -0
  353. package/skills/cloudflare/references/workers-for-platforms/README.md +95 -0
  354. package/skills/cloudflare/references/workers-for-platforms/api.md +212 -0
  355. package/skills/cloudflare/references/workers-for-platforms/configuration.md +178 -0
  356. package/skills/cloudflare/references/workers-for-platforms/gotchas.md +134 -0
  357. package/skills/cloudflare/references/workers-for-platforms/patterns.md +210 -0
  358. package/skills/cloudflare/references/workers-playground/README.md +131 -0
  359. package/skills/cloudflare/references/workers-playground/api.md +101 -0
  360. package/skills/cloudflare/references/workers-playground/configuration.md +169 -0
  361. package/skills/cloudflare/references/workers-playground/gotchas.md +88 -0
  362. package/skills/cloudflare/references/workers-playground/patterns.md +134 -0
  363. package/skills/cloudflare/references/workers-vpc/README.md +130 -0
  364. package/skills/cloudflare/references/workers-vpc/api.md +196 -0
  365. package/skills/cloudflare/references/workers-vpc/configuration.md +151 -0
  366. package/skills/cloudflare/references/workers-vpc/gotchas.md +171 -0
  367. package/skills/cloudflare/references/workers-vpc/patterns.md +235 -0
  368. package/skills/cloudflare/references/workflows/README.md +72 -0
  369. package/skills/cloudflare/references/workflows/api.md +237 -0
  370. package/skills/cloudflare/references/workflows/configuration.md +158 -0
  371. package/skills/cloudflare/references/workflows/gotchas.md +97 -0
  372. package/skills/cloudflare/references/workflows/patterns.md +245 -0
  373. package/skills/cloudflare/references/wrangler/README.md +143 -0
  374. package/skills/cloudflare/references/wrangler/api.md +188 -0
  375. package/skills/cloudflare/references/wrangler/configuration.md +198 -0
  376. package/skills/cloudflare/references/wrangler/gotchas.md +212 -0
  377. package/skills/cloudflare/references/wrangler/patterns.md +211 -0
  378. package/skills/cloudflare/references/zaraz/IMPLEMENTATION_SUMMARY.md +131 -0
  379. package/skills/cloudflare/references/zaraz/README.md +114 -0
  380. package/skills/cloudflare/references/zaraz/api.md +118 -0
  381. package/skills/cloudflare/references/zaraz/configuration.md +94 -0
  382. package/skills/cloudflare/references/zaraz/gotchas.md +88 -0
  383. package/skills/cloudflare/references/zaraz/patterns.md +77 -0
  384. package/skills/docker/SKILL.md +7 -101
  385. package/skills/docker/references/advanced-examples.md +71 -0
  386. package/skills/docker/references/templates.md +34 -0
  387. package/skills/docs-marketer/SKILL.md +178 -0
  388. package/skills/docs-marketer/references/audit-categories.md +328 -0
  389. package/skills/docs-marketer/references/copilot-docs-prompts.md +88 -0
  390. package/skills/docs-marketer/references/copilot-usage.md +16 -0
  391. package/skills/docs-marketer/references/feedback-loop.md +155 -0
  392. package/skills/docs-marketer/references/multi-pass-docs-protocol.md +410 -0
  393. package/skills/drizzle-orm/SKILL.md +82 -0
  394. package/skills/durable-objects/SKILL.md +167 -0
  395. package/skills/durable-objects/references/advanced_features.md +29 -0
  396. package/skills/durable-objects/references/rules.md +300 -0
  397. package/skills/durable-objects/references/testing.md +261 -0
  398. package/skills/durable-objects/references/workers.md +336 -0
  399. package/skills/gcp/SKILL.md +37 -0
  400. package/skills/github-actions/SKILL.md +5 -58
  401. package/skills/github-actions/references/templates.md +65 -0
  402. package/skills/github-commander/SKILL.md +13 -21
  403. package/skills/github-commander/workflows/copilot-audit.md +12 -12
  404. package/skills/github-copilot-cli/SKILL.md +21 -26
  405. package/skills/github-repo-setup/SKILL.md +136 -0
  406. package/skills/github-repo-setup/references/community-standards.md +136 -0
  407. package/skills/github-repo-setup/references/github-automation.md +490 -0
  408. package/skills/github-repo-setup/references/inline-templates.md +205 -0
  409. package/skills/github-repo-setup/references/project-config.md +320 -0
  410. package/skills/gitlab/SKILL.md +7 -2
  411. package/skills/gitlab/package-lock.json +389 -389
  412. package/skills/golang/SKILL.md +8 -1
  413. package/skills/graphql/SKILL.md +30 -0
  414. package/skills/hono/SKILL.md +82 -0
  415. package/skills/journal-optimizer/SKILL.md +206 -0
  416. package/skills/journal-optimizer/references/optimizer-scripts.md +169 -0
  417. package/skills/llm-app-engineering/SKILL.md +18 -0
  418. package/skills/monorepo/SKILL.md +56 -0
  419. package/skills/multi-agent-orchestration/SKILL.md +14 -0
  420. package/skills/mysql/SKILL.md +6 -2
  421. package/skills/next-best-practices/SKILL.md +86 -0
  422. package/skills/next-best-practices/references/cache-components-examples.md +234 -0
  423. package/skills/next-best-practices/references/cache-components.md +210 -0
  424. package/skills/next-best-practices/references/upgrade-decision-tree.md +33 -0
  425. package/skills/next-best-practices/references/upgrade.md +43 -0
  426. package/skills/next-cache-components/SKILL.md +441 -0
  427. package/skills/next-upgrade/SKILL.md +43 -0
  428. package/skills/next-upgrade/references/decision-tree.md +33 -0
  429. package/skills/nodejs/SKILL.md +46 -0
  430. package/skills/opentelemetry/SKILL.md +62 -0
  431. package/skills/package.json +39 -4
  432. package/skills/playwright-standard/SKILL.md +6 -11
  433. package/skills/playwright-standard/references/locators.md +7 -0
  434. package/skills/postgres/SKILL.md +6 -1
  435. package/skills/python/SKILL.md +8 -70
  436. package/skills/python/references/advanced-patterns.md +37 -0
  437. package/skills/python/references/config-templates.md +48 -0
  438. package/skills/rag-pipelines/SKILL.md +14 -0
  439. package/skills/redis/SKILL.md +31 -0
  440. package/skills/render/SKILL.md +35 -0
  441. package/skills/rust/SKILL.md +15 -25
  442. package/skills/rust/references/borrow-checker.md +13 -0
  443. package/skills/rust/references/ecosystem.md +11 -0
  444. package/skills/sandbox-sdk/SKILL.md +186 -0
  445. package/skills/sandbox-sdk/references/api-quick-ref.md +113 -0
  446. package/skills/sandbox-sdk/references/examples.md +52 -0
  447. package/skills/shadcn-ui/SKILL.md +22 -57
  448. package/skills/skill-builder/SKILL.md +23 -424
  449. package/skills/skill-builder/references/tutorial.md +457 -0
  450. package/skills/sqlite/SKILL.md +16 -5
  451. package/skills/table.md +59 -0
  452. package/skills/tailwind-css/SKILL.md +11 -60
  453. package/skills/tailwind-css/references/component-patterns.md +52 -0
  454. package/skills/trpc/SKILL.md +56 -0
  455. package/skills/typescript/SKILL.md +30 -433
  456. package/skills/typescript/references/tutorial.md +453 -0
  457. package/skills/vercel-ai-sdk/SKILL.md +48 -0
  458. package/skills/vitest-standard/SKILL.md +5 -11
  459. package/skills/vitest-standard/references/assertions.md +11 -0
  460. package/skills/web-perf/SKILL.md +207 -0
  461. package/skills/workers-best-practices/SKILL.md +120 -0
  462. package/skills/workers-best-practices/references/anti-patterns.md +18 -0
  463. package/skills/workers-best-practices/references/review.md +174 -0
  464. package/skills/workers-best-practices/references/rules.md +485 -0
  465. package/skills/wrangler/SKILL.md +43 -0
  466. package/skills/wrangler/references/cli-commands.md +861 -0
  467. package/skills/zod/SKILL.md +48 -0
  468. package/dist/tools-P4VGG4FH.js +0 -1
  469. package/skills/react-best-practices/AGENTS.md +0 -2883
  470. package/skills/react-best-practices/SKILL.md +0 -138
  471. /package/skills/{react-best-practices → next-best-practices}/README.md +0 -0
  472. /package/skills/{react-best-practices → next-best-practices}/metadata.json +0 -0
  473. /package/skills/{react-best-practices → next-best-practices}/rules/_sections.md +0 -0
  474. /package/skills/{react-best-practices → next-best-practices}/rules/_template.md +0 -0
  475. /package/skills/{react-best-practices → next-best-practices}/rules/advanced-event-handler-refs.md +0 -0
  476. /package/skills/{react-best-practices → next-best-practices}/rules/advanced-init-once.md +0 -0
  477. /package/skills/{react-best-practices → next-best-practices}/rules/advanced-use-latest.md +0 -0
  478. /package/skills/{react-best-practices → next-best-practices}/rules/async-api-routes.md +0 -0
  479. /package/skills/{react-best-practices → next-best-practices}/rules/async-defer-await.md +0 -0
  480. /package/skills/{react-best-practices → next-best-practices}/rules/async-dependencies.md +0 -0
  481. /package/skills/{react-best-practices → next-best-practices}/rules/async-parallel.md +0 -0
  482. /package/skills/{react-best-practices → next-best-practices}/rules/async-suspense-boundaries.md +0 -0
  483. /package/skills/{react-best-practices → next-best-practices}/rules/bundle-barrel-imports.md +0 -0
  484. /package/skills/{react-best-practices → next-best-practices}/rules/bundle-conditional.md +0 -0
  485. /package/skills/{react-best-practices → next-best-practices}/rules/bundle-defer-third-party.md +0 -0
  486. /package/skills/{react-best-practices → next-best-practices}/rules/bundle-dynamic-imports.md +0 -0
  487. /package/skills/{react-best-practices → next-best-practices}/rules/bundle-preload.md +0 -0
  488. /package/skills/{react-best-practices → next-best-practices}/rules/client-event-listeners.md +0 -0
  489. /package/skills/{react-best-practices → next-best-practices}/rules/client-localstorage-schema.md +0 -0
  490. /package/skills/{react-best-practices → next-best-practices}/rules/client-passive-event-listeners.md +0 -0
  491. /package/skills/{react-best-practices → next-best-practices}/rules/client-swr-dedup.md +0 -0
  492. /package/skills/{react-best-practices → next-best-practices}/rules/js-batch-dom-css.md +0 -0
  493. /package/skills/{react-best-practices → next-best-practices}/rules/js-cache-function-results.md +0 -0
  494. /package/skills/{react-best-practices → next-best-practices}/rules/js-cache-property-access.md +0 -0
  495. /package/skills/{react-best-practices → next-best-practices}/rules/js-cache-storage.md +0 -0
  496. /package/skills/{react-best-practices → next-best-practices}/rules/js-combine-iterations.md +0 -0
  497. /package/skills/{react-best-practices → next-best-practices}/rules/js-early-exit.md +0 -0
  498. /package/skills/{react-best-practices → next-best-practices}/rules/js-hoist-regexp.md +0 -0
  499. /package/skills/{react-best-practices → next-best-practices}/rules/js-index-maps.md +0 -0
  500. /package/skills/{react-best-practices → next-best-practices}/rules/js-length-check-first.md +0 -0
  501. /package/skills/{react-best-practices → next-best-practices}/rules/js-min-max-loop.md +0 -0
  502. /package/skills/{react-best-practices → next-best-practices}/rules/js-set-map-lookups.md +0 -0
  503. /package/skills/{react-best-practices → next-best-practices}/rules/js-tosorted-immutable.md +0 -0
  504. /package/skills/{react-best-practices → next-best-practices}/rules/rendering-activity.md +0 -0
  505. /package/skills/{react-best-practices → next-best-practices}/rules/rendering-animate-svg-wrapper.md +0 -0
  506. /package/skills/{react-best-practices → next-best-practices}/rules/rendering-conditional-render.md +0 -0
  507. /package/skills/{react-best-practices → next-best-practices}/rules/rendering-content-visibility.md +0 -0
  508. /package/skills/{react-best-practices → next-best-practices}/rules/rendering-hoist-jsx.md +0 -0
  509. /package/skills/{react-best-practices → next-best-practices}/rules/rendering-hydration-no-flicker.md +0 -0
  510. /package/skills/{react-best-practices → next-best-practices}/rules/rendering-hydration-suppress-warning.md +0 -0
  511. /package/skills/{react-best-practices → next-best-practices}/rules/rendering-svg-precision.md +0 -0
  512. /package/skills/{react-best-practices → next-best-practices}/rules/rendering-usetransition-loading.md +0 -0
  513. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-defer-reads.md +0 -0
  514. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-dependencies.md +0 -0
  515. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-derived-state-no-effect.md +0 -0
  516. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-derived-state.md +0 -0
  517. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-functional-setstate.md +0 -0
  518. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-lazy-state-init.md +0 -0
  519. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-memo-with-default-value.md +0 -0
  520. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-memo.md +0 -0
  521. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-move-effect-to-event.md +0 -0
  522. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-simple-expression-in-memo.md +0 -0
  523. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-transitions.md +0 -0
  524. /package/skills/{react-best-practices → next-best-practices}/rules/rerender-use-ref-transient-values.md +0 -0
  525. /package/skills/{react-best-practices → next-best-practices}/rules/server-after-nonblocking.md +0 -0
  526. /package/skills/{react-best-practices → next-best-practices}/rules/server-auth-actions.md +0 -0
  527. /package/skills/{react-best-practices → next-best-practices}/rules/server-cache-lru.md +0 -0
  528. /package/skills/{react-best-practices → next-best-practices}/rules/server-cache-react.md +0 -0
  529. /package/skills/{react-best-practices → next-best-practices}/rules/server-dedup-props.md +0 -0
  530. /package/skills/{react-best-practices → next-best-practices}/rules/server-parallel-fetching.md +0 -0
  531. /package/skills/{react-best-practices → next-best-practices}/rules/server-serialization.md +0 -0
@@ -1,16 +1,13 @@
1
- import { TOOL_GROUPS, logger, getGitHubIntegration, parseToolFilter, getFilterSummary, getToolFilterFromEnv, getTools, getRequiredScope, getEnabledGroups, callTool, MetricsAccumulator, getAuthContext, getRequestContext, ConfigurationError, sendProgress, SUPPORTED_SCOPES, enforceAccessBoundary, runWithAuthContext, isValidScope, requestContextStorage, ConnectionError, ResourceNotFoundError, QueryError, assertNoPathTraversal, ValidationError, resolveGitHubRepo, isResourceError, markUntrustedContentInline, milestoneCompletionPct, sanitizeAuthor, markUntrustedContent, parseFlagContext, DEFAULT_FLAG_VOCABULARY, parseScopes, BASE_SCOPES, validateDateFormatPattern, getAllToolNames, assertSafeFilePath, assertSafeDirectoryPath, MemoryJournalMcpError, sanitizeSearchQuery, DEFAULT_BRIEFING_CONFIG } from './chunk-ARLYSFSI.js';
2
- import { createRequire } from 'module';
3
- import { appendFile, open, mkdir, stat, rename } from 'fs/promises';
4
- import * as path4 from 'path';
5
- import { dirname } from 'path';
6
- import { performance } from 'perf_hooks';
1
+ import { getTools, callTool, MetricsAccumulator, sendProgress } from './chunk-UHSO65A4.js';
2
+ import { AuditLogger, createAuditInterceptor, getResources, getPrompts, generateInstructions, VERSION, auditOperation, getPrompt, readResource } from './chunk-JFMITANR.js';
3
+ import { logger, getGitHubIntegration, parseToolFilter, getFilterSummary, getToolFilterFromEnv, getRequiredScope, getEnabledGroups, ConfigurationError, SUPPORTED_SCOPES, enforceAccessBoundary, runWithAuthContext, isValidScope, requestContextStorage, ConnectionError, ResourceNotFoundError, QueryError, assertNoPathTraversal, ValidationError, parseScopes, BASE_SCOPES, validateDateFormatPattern, MemoryJournalMcpError, sanitizeSearchQuery, DEFAULT_BRIEFING_CONFIG } from './chunk-6OHRCNYW.js';
7
4
  import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
8
5
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
9
6
  import DatabaseAdapter from 'better-sqlite3';
10
7
  import * as fs2 from 'fs';
8
+ import * as path2 from 'path';
11
9
  import { execFile } from 'child_process';
12
10
  import { createHash, timingSafeEqual, randomUUID } from 'crypto';
13
- import { fileURLToPath } from 'url';
14
11
  import express from 'express';
15
12
  import 'https';
16
13
  import * as jose from 'jose';
@@ -20,473 +17,6 @@ import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
20
17
  import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
21
18
  import { z } from 'zod';
22
19
 
23
- var require2 = createRequire(import.meta.url);
24
- var pkg = require2("../package.json");
25
- var VERSION = pkg.version;
26
-
27
- // src/audit/types.ts
28
- var DEFAULT_AUDIT_LOG_MAX_SIZE_BYTES = 10 * 1024 * 1024;
29
- var BUFFER_HIGH_WATER = 50;
30
- var FLUSH_INTERVAL_MS = 100;
31
- var DEFAULT_RECENT_COUNT = 50;
32
- var STDERR_SENTINEL = "stderr";
33
- var TAIL_READ_BYTES = 65536;
34
- var MAX_ARCHIVES = 5;
35
- var AuditLogger = class {
36
- config;
37
- buffer = [];
38
- flushTimer = null;
39
- flushQueue = Promise.resolve();
40
- closed = false;
41
- dirEnsured = false;
42
- stderrMode;
43
- _droppedCount = 0;
44
- constructor(config) {
45
- this.config = config;
46
- this.stderrMode = config.logPath.toLowerCase() === STDERR_SENTINEL;
47
- if (config.enabled) {
48
- this.flushTimer = setInterval(() => {
49
- void this.flush();
50
- }, FLUSH_INTERVAL_MS);
51
- this.flushTimer.unref();
52
- }
53
- }
54
- /**
55
- * Total number of events dropped due to memory limits or persistent I/O failure.
56
- */
57
- get droppedCount() {
58
- return this._droppedCount;
59
- }
60
- /**
61
- * Append an audit entry to the buffer.
62
- * Non-blocking — the entry is serialised and queued; the
63
- * actual file write happens on the next flush cycle.
64
- *
65
- * NOTE: This is a lossy operational telemetry mechanism, not a guaranteed immutable ledger.
66
- * Under extreme backpressure or persistent I/O failure, oldest entries will be dropped
67
- * to preserve memory and system stability.
68
- */
69
- log(entry) {
70
- if (this.closed || !this.config.enabled) return;
71
- this.buffer.push(JSON.stringify(entry));
72
- if (this.buffer.length > 5e3) {
73
- this.buffer.shift();
74
- if (this._droppedCount === 0) {
75
- logger.warning(
76
- "Telemetry buffer overflow. Dropping oldest entries. Note: This log is a lossy operational telemetry mechanism.",
77
- { module: "Audit" }
78
- );
79
- }
80
- this._droppedCount++;
81
- }
82
- if (this.buffer.length >= BUFFER_HIGH_WATER) {
83
- void this.flush();
84
- }
85
- }
86
- /**
87
- * Log a denied access attempt.
88
- */
89
- logDenial(toolName, reason, context) {
90
- this.log({
91
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
92
- requestId: context?.requestId ?? `denied-${Date.now().toString()}`,
93
- sessionId: context?.sessionId ?? void 0,
94
- tool: toolName,
95
- category: context?.category ?? "read",
96
- scope: context?.scope ?? "",
97
- user: context?.user ?? null,
98
- scopes: context?.scopes ?? [],
99
- durationMs: 0,
100
- success: false,
101
- error: `Access Denied: ${reason}`
102
- });
103
- }
104
- /**
105
- * Flush the buffer to disk.
106
- * Safe to call concurrently — serialises via `this.flushQueue` Promise chain.
107
- */
108
- async flush() {
109
- if (this.buffer.length === 0) return;
110
- this.flushQueue = this.flushQueue.then(async () => {
111
- if (this.buffer.length === 0) return;
112
- await this.rotateIfNeeded();
113
- const lines = this.buffer;
114
- this.buffer = [];
115
- try {
116
- if (this.stderrMode) {
117
- process.stderr.write(lines.join("\n") + "\n");
118
- } else {
119
- await this.ensureDirectory();
120
- await appendFile(this.config.logPath, lines.join("\n") + "\n", "utf-8");
121
- }
122
- } catch (err) {
123
- const message = err instanceof Error ? err.message : String(err);
124
- logger.error(`Write failed: ${message}`, { module: "Audit" });
125
- this.buffer.unshift(...lines);
126
- if (this.buffer.length > 5e3) {
127
- logger.error(
128
- `Buffer overflow (${String(this.buffer.length)} entries), dropping oldest entries`,
129
- { module: "Audit" }
130
- );
131
- const toDrop = this.buffer.length - 5e3;
132
- this.buffer = this.buffer.slice(-5e3);
133
- this._droppedCount += toDrop;
134
- }
135
- }
136
- }).catch(() => {
137
- });
138
- await this.flushQueue;
139
- }
140
- /**
141
- * Gracefully close the logger — flush remaining entries and stop the timer.
142
- */
143
- async close() {
144
- this.closed = true;
145
- if (this.flushTimer) {
146
- clearInterval(this.flushTimer);
147
- this.flushTimer = null;
148
- }
149
- await this.flush();
150
- }
151
- /**
152
- * Read the most recent audit entries from the log file.
153
- * Uses a streaming tail-read: only the last TAIL_READ_BYTES (64 KB) are
154
- * read from disk, preventing O(n) memory spikes for large audit logs.
155
- * Used by the `memory://audit` resource.
156
- *
157
- * @param count Maximum number of entries to return (default 50)
158
- */
159
- async recent(count = DEFAULT_RECENT_COUNT) {
160
- if (this.stderrMode) return [];
161
- await this.flush();
162
- try {
163
- let fh;
164
- try {
165
- fh = await open(this.config.logPath, "r");
166
- } catch {
167
- return [];
168
- }
169
- try {
170
- const info = await fh.stat();
171
- if (info.isDirectory()) return [];
172
- const fileSize = info.size;
173
- if (fileSize === 0) return [];
174
- const readSize = Math.min(fileSize, TAIL_READ_BYTES);
175
- const startOffset = fileSize - readSize;
176
- const buf = Buffer.alloc(readSize);
177
- await fh.read(buf, 0, readSize, startOffset);
178
- const chunk = buf.toString("utf-8");
179
- const rawLines = chunk.split("\n").filter(Boolean);
180
- const lines = startOffset > 0 ? rawLines.slice(1) : rawLines;
181
- const tail = lines.slice(-count);
182
- return tail.reduce((acc, line) => {
183
- try {
184
- acc.push(JSON.parse(line));
185
- } catch {
186
- }
187
- return acc;
188
- }, []);
189
- } finally {
190
- await fh.close();
191
- }
192
- } catch (err) {
193
- throw new Error(
194
- `Failed to read telemetry log: ${err instanceof Error ? err.message : String(err)}`,
195
- { cause: err }
196
- );
197
- }
198
- }
199
- // =========================================================================
200
- // Private helpers
201
- // =========================================================================
202
- /**
203
- * Ensure the parent directory of the log file exists.
204
- */
205
- async ensureDirectory() {
206
- if (this.dirEnsured) return;
207
- try {
208
- await mkdir(dirname(this.config.logPath), { recursive: true });
209
- this.dirEnsured = true;
210
- } catch {
211
- this.dirEnsured = true;
212
- }
213
- }
214
- /**
215
- * Rotate the log file if it exceeds the configured size limit.
216
- * Keeps up to 5 rotated files (`.1` through `.5`); older data is discarded.
217
- * Rotation failure is non-fatal — audit must not block tool execution.
218
- */
219
- async rotateIfNeeded() {
220
- if (this.stderrMode || !this.config.maxSizeBytes) return;
221
- try {
222
- const info = await stat(this.config.logPath).catch(() => null);
223
- if (!info || info.size < this.config.maxSizeBytes) return;
224
- for (let i = MAX_ARCHIVES - 1; i >= 1; i--) {
225
- const oldFile = `${this.config.logPath}.${String(i)}`;
226
- const newFile = `${this.config.logPath}.${String(i + 1)}`;
227
- await rename(oldFile, newFile).catch(() => null);
228
- }
229
- const rotatedPath = `${this.config.logPath}.1`;
230
- await rename(this.config.logPath, rotatedPath);
231
- } catch (err) {
232
- const message = err instanceof Error ? err.message : String(err);
233
- logger.error(`Rotate failed: ${message}`, { module: "Audit" });
234
- }
235
- }
236
- };
237
- var ALWAYS_AUDITED_SCOPES = /* @__PURE__ */ new Set(["write", "admin", "team", "audit"]);
238
- function scopeToCategory(scope) {
239
- if (scope === "admin") return "admin";
240
- if (scope === "team") return "team";
241
- if (scope === "audit") return "audit";
242
- if (scope === "read") return "read";
243
- return "write";
244
- }
245
- function generateRequestId() {
246
- const ts = Date.now().toString(36);
247
- const rand = Math.random().toString(36).slice(2, 8);
248
- return `aud-${ts}-${rand}`;
249
- }
250
- function createAuditInterceptor(auditLogger) {
251
- const auditReads = auditLogger.config.auditReads;
252
- return {
253
- async around(toolName, args, fn) {
254
- const scope = getRequiredScope(toolName);
255
- if (!ALWAYS_AUDITED_SCOPES.has(scope) && !auditReads) {
256
- return fn();
257
- }
258
- const isReadScope = scope === "read";
259
- const authCtx = getAuthContext();
260
- const reqCtx = getRequestContext();
261
- const user = authCtx?.claims?.sub ?? null;
262
- const scopes = authCtx?.claims?.scopes ?? [];
263
- const sessionId = reqCtx?.sessionId;
264
- const requestId = generateRequestId();
265
- const start = performance.now();
266
- let success = true;
267
- let error;
268
- let tokenEstimate;
269
- try {
270
- const result = await fn();
271
- if (typeof result === "object" && result !== null) {
272
- try {
273
- const json = JSON.stringify({
274
- ...result,
275
- _meta: { tokenEstimate: 0 }
276
- });
277
- tokenEstimate = Math.ceil(Buffer.byteLength(json, "utf8") / 4);
278
- } catch {
279
- }
280
- } else if (typeof result === "string") {
281
- tokenEstimate = Math.ceil(Buffer.byteLength(result, "utf8") / 4);
282
- }
283
- return result;
284
- } catch (err) {
285
- success = false;
286
- error = err instanceof Error ? err.message : String(err);
287
- const errorResult = {
288
- success: false,
289
- error,
290
- code: "INTERNAL_ERROR",
291
- category: "internal",
292
- recoverable: false
293
- };
294
- const enriched = JSON.stringify({
295
- ...errorResult,
296
- _meta: { tokenEstimate: 0 }
297
- });
298
- tokenEstimate = Math.ceil(Buffer.byteLength(enriched, "utf8") / 4);
299
- throw err;
300
- } finally {
301
- const durationMs = Math.round(performance.now() - start);
302
- if (isReadScope) {
303
- auditLogger.log({
304
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
305
- requestId,
306
- tool: toolName,
307
- category: "read",
308
- scope,
309
- user,
310
- scopes,
311
- sessionId,
312
- durationMs,
313
- success,
314
- error,
315
- tokenEstimate
316
- });
317
- } else {
318
- auditLogger.log({
319
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
320
- requestId,
321
- tool: toolName,
322
- category: scopeToCategory(scope),
323
- scope,
324
- user,
325
- scopes,
326
- sessionId,
327
- durationMs,
328
- success,
329
- error,
330
- args: auditLogger.config.redact ? void 0 : args,
331
- tokenEstimate
332
- });
333
- }
334
- }
335
- }
336
- };
337
- }
338
- async function auditOperation(auditLogger, operationType, name, fn) {
339
- if (!auditLogger?.config.auditReads) {
340
- return Promise.resolve(fn());
341
- }
342
- const authCtx = getAuthContext();
343
- const reqCtx = getRequestContext();
344
- const user = authCtx?.claims?.sub ?? null;
345
- const scopes = authCtx?.claims?.scopes ?? [];
346
- const sessionId = reqCtx?.sessionId;
347
- const requestId = generateRequestId();
348
- const start = performance.now();
349
- let success = true;
350
- let error;
351
- let tokenEstimate;
352
- try {
353
- const result = await fn();
354
- if (typeof result === "object" && result !== null) {
355
- try {
356
- const json = JSON.stringify(result);
357
- tokenEstimate = Math.ceil(Buffer.byteLength(json, "utf8") / 4);
358
- } catch {
359
- }
360
- } else if (typeof result === "string") {
361
- tokenEstimate = Math.ceil(Buffer.byteLength(result, "utf8") / 4);
362
- }
363
- return result;
364
- } catch (err) {
365
- success = false;
366
- error = err instanceof Error ? err.message : String(err);
367
- throw err;
368
- } finally {
369
- const durationMs = Math.round(performance.now() - start);
370
- auditLogger.log({
371
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
372
- requestId,
373
- sessionId,
374
- tool: `${operationType}:${name}`,
375
- category: "read",
376
- scope: "read",
377
- user,
378
- scopes,
379
- durationMs,
380
- success,
381
- error,
382
- tokenEstimate
383
- });
384
- }
385
- }
386
-
387
- // src/utils/resource-annotations.ts
388
- var HIGH_PRIORITY = {
389
- priority: 0.9,
390
- audience: ["user", "assistant"]
391
- };
392
- var MEDIUM_PRIORITY = {
393
- priority: 0.6,
394
- audience: ["user", "assistant"]
395
- };
396
- var LOW_PRIORITY = {
397
- priority: 0.4,
398
- audience: ["user", "assistant"]
399
- };
400
- var ASSISTANT_FOCUSED = {
401
- priority: 0.5,
402
- audience: ["assistant"]
403
- };
404
- function withPriority(priority, base = MEDIUM_PRIORITY) {
405
- return { ...base, priority };
406
- }
407
- function withSessionInit(base = HIGH_PRIORITY) {
408
- return { ...base, sessionInit: true };
409
- }
410
-
411
- // src/audit/audit-resource.ts
412
- function getAuditResourceDef(getLogger) {
413
- return {
414
- uri: "memory://audit",
415
- name: "Audit Log",
416
- title: "Operational Telemetry Log (last 50 entries)",
417
- description: "Last 50 write/admin tool call telemetry entries from the JSONL log. Each entry includes tool name, scope, duration, token estimates, and error status. Includes a session summary with total token consumption and error count.",
418
- mimeType: "text/plain",
419
- annotations: {
420
- ...ASSISTANT_FOCUSED
421
- },
422
- capabilities: {
423
- requiresAdminScope: true
424
- },
425
- handler: async (_uri, _context) => {
426
- const lastModified = (/* @__PURE__ */ new Date()).toISOString();
427
- const auditLogger = getLogger();
428
- if (!auditLogger) {
429
- return {
430
- data: "audit: not configured\nhint: Set AUDIT_LOG_PATH env var or --audit-log CLI flag to enable audit logging.",
431
- annotations: { lastModified }
432
- };
433
- }
434
- const entries = await auditLogger.recent(50);
435
- if (entries.length === 0) {
436
- return {
437
- data: `audit_log: ${auditLogger.config.logPath}
438
- entries: 0
439
- note: No write/admin operations have been audited yet.`,
440
- annotations: { lastModified }
441
- };
442
- }
443
- let totalTokens = 0;
444
- let errorCount = 0;
445
- let totalDuration = 0;
446
- for (const e of entries) {
447
- totalTokens += e.tokenEstimate ?? 0;
448
- totalDuration += e.durationMs;
449
- if (!e.success) errorCount++;
450
- }
451
- const formattedEntries = entries.map((e) => {
452
- const parts = [
453
- `- timestamp: ${e.timestamp}`,
454
- ` tool: ${e.tool}`,
455
- ` scope: ${e.scope}`,
456
- ` category: ${e.category}`,
457
- ` duration_ms: ${String(e.durationMs)}`,
458
- ` success: ${String(e.success)}`
459
- ];
460
- if (e.error) {
461
- parts.push(` error: ${e.error}`);
462
- }
463
- if (e.tokenEstimate !== void 0) {
464
- parts.push(` token_estimate: ${String(e.tokenEstimate)}`);
465
- }
466
- if (e.args !== void 0) {
467
- parts.push(` args: ${JSON.stringify(e.args)}`);
468
- }
469
- return parts.join("\n");
470
- }).join("\n");
471
- const text = `audit_log: ${auditLogger.config.logPath}
472
- entries_shown: ${String(entries.length)}
473
- as_of: ${lastModified}
474
- session_summary:
475
- total_tokens: ${String(totalTokens)}
476
- total_duration_ms: ${String(totalDuration)}
477
- error_count: ${String(errorCount)}
478
- dropped_count: ${String(auditLogger.droppedCount)}
479
- redact_mode: ${String(auditLogger.config.redact)}
480
-
481
- ` + formattedEntries;
482
- return {
483
- data: text,
484
- annotations: { lastModified }
485
- };
486
- }
487
- };
488
- }
489
-
490
20
  // src/database/core/schema.ts
491
21
  var SCHEMA_SQL = `
492
22
  -- Main journal entries table
@@ -641,7 +171,7 @@ var NativeConnectionManager = class {
641
171
  }
642
172
  async initialize() {
643
173
  if (this.initialized) return;
644
- const dir = path4.dirname(this.dbPath);
174
+ const dir = path2.dirname(this.dbPath);
645
175
  if (dir && !fs2.existsSync(dir)) {
646
176
  await fs2.promises.mkdir(dir, { recursive: true });
647
177
  }
@@ -850,7 +380,7 @@ var NativeConnectionManager = class {
850
380
  return this.dbPath;
851
381
  }
852
382
  getBackupsDir() {
853
- return path4.join(path4.dirname(this.dbPath), "backups");
383
+ return path2.join(path2.dirname(this.dbPath), "backups");
854
384
  }
855
385
  closeDbBeforeRestore() {
856
386
  this.close();
@@ -967,7 +497,6 @@ var TagsManager = class {
967
497
  const existingRows = db.prepare("SELECT entry_id FROM entry_tags WHERE tag_id = ?").all(targetTagId);
968
498
  const existingEntryIds = new Set(existingRows.map((r) => r.entry_id));
969
499
  const newEntryIds = entryIds.filter((id) => !existingEntryIds.has(id));
970
- const entriesUpdated = newEntryIds.length;
971
500
  if (newEntryIds.length > 0) {
972
501
  const placeholders = newEntryIds.map(() => "(?, ?)").join(", ");
973
502
  const params = newEntryIds.flatMap((entryId) => [entryId, targetTagId]);
@@ -975,15 +504,15 @@ var TagsManager = class {
975
504
  `INSERT OR IGNORE INTO entry_tags (entry_id, tag_id) VALUES ${placeholders}`
976
505
  ).run(...params);
977
506
  }
978
- if (entriesUpdated > 0) {
507
+ if (newEntryIds.length > 0) {
979
508
  db.prepare("UPDATE tags SET usage_count = usage_count + ? WHERE id = ?").run(
980
- entriesUpdated,
509
+ newEntryIds.length,
981
510
  targetTagId
982
511
  );
983
512
  }
984
513
  db.prepare("DELETE FROM entry_tags WHERE tag_id = ?").run(sourceTagId);
985
514
  db.prepare("DELETE FROM tags WHERE id = ?").run(sourceTagId);
986
- return { entriesUpdated, sourceDeleted: true };
515
+ return { entriesUpdated: newEntryIds.length, sourceDeleted: true };
987
516
  });
988
517
  const result = mergeOp();
989
518
  this.ctx.scheduleSave();
@@ -1100,7 +629,10 @@ function createEntry(context, input) {
1100
629
  const result = stmt.run(...values);
1101
630
  insertId = result.lastInsertRowid;
1102
631
  if (input.tags && input.tags.length > 0) {
1103
- tagsMgr.linkTagsToEntry(insertId, input.tags);
632
+ const formattedTags = input.tags.map(
633
+ (t) => t.toLowerCase().replace(/[^a-z0-9:@]+/g, "-").replace(/^-+|-+$/g, "")
634
+ );
635
+ tagsMgr.linkTagsToEntry(insertId, formattedTags);
1104
636
  }
1105
637
  });
1106
638
  txn();
@@ -1216,7 +748,10 @@ function updateEntry(context, id, input) {
1216
748
  }
1217
749
  if (input.tags !== void 0) {
1218
750
  db.prepare("DELETE FROM entry_tags WHERE entry_id = ?").run(id);
1219
- tagsMgr.linkTagsToEntry(id, input.tags);
751
+ const formattedTags = input.tags.map(
752
+ (t) => t.toLowerCase().replace(/[^a-z0-9:@]+/g, "-").replace(/^-+|-+$/g, "")
753
+ );
754
+ tagsMgr.linkTagsToEntry(id, formattedTags);
1220
755
  }
1221
756
  return getEntryById(context, id);
1222
757
  }
@@ -1922,7 +1457,7 @@ var BackupManager = class {
1922
1457
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1923
1458
  const sanitizedName = backupName ? backupName.replace(/[/\\:*?"<>|]/g, "_").slice(0, MAX_BACKUP_NAME_LENGTH) : `backup_${timestamp}`;
1924
1459
  const filename = `${sanitizedName}.db`;
1925
- const backupPath = path4.join(backupsDir, filename);
1460
+ const backupPath = path2.join(backupsDir, filename);
1926
1461
  try {
1927
1462
  this.ctx.pragma("wal_checkpoint(TRUNCATE)");
1928
1463
  } catch (checkpointErr) {
@@ -1954,7 +1489,7 @@ var BackupManager = class {
1954
1489
  const backups = [];
1955
1490
  for (const filename of files) {
1956
1491
  if (!filename.endsWith(".db")) continue;
1957
- const filePath = path4.join(backupsDir, filename);
1492
+ const filePath = path2.join(backupsDir, filename);
1958
1493
  try {
1959
1494
  const stats = fs2.statSync(filePath);
1960
1495
  if (stats.isFile()) {
@@ -2010,20 +1545,20 @@ var BackupManager = class {
2010
1545
  async restoreFromFile(filename, runtime) {
2011
1546
  assertNoPathTraversal(filename);
2012
1547
  const backupsDir = this.ctx.getBackupsDir();
2013
- const backupPath = path4.resolve(backupsDir, filename);
2014
- if (!backupPath.startsWith(path4.resolve(backupsDir))) {
1548
+ const backupPath = path2.resolve(backupsDir, filename);
1549
+ if (!backupPath.startsWith(path2.resolve(backupsDir))) {
2015
1550
  throw new ValidationError(`Path traversal detected during restore resolution.`);
2016
1551
  }
2017
- let stat2;
1552
+ let stat;
2018
1553
  try {
2019
- stat2 = await fs2.promises.lstat(backupPath);
1554
+ stat = await fs2.promises.lstat(backupPath);
2020
1555
  } catch (e) {
2021
1556
  if (e instanceof Error && "code" in e && e.code === "ENOENT") {
2022
1557
  throw new ResourceNotFoundError("Backup", filename);
2023
1558
  }
2024
1559
  throw e;
2025
1560
  }
2026
- if (stat2.isSymbolicLink()) {
1561
+ if (stat.isSymbolicLink()) {
2027
1562
  throw new ValidationError("Symlinks are not allowed for backup restore.");
2028
1563
  }
2029
1564
  const realBackupPath = await fs2.promises.realpath(backupPath);
@@ -2049,7 +1584,7 @@ var BackupManager = class {
2049
1584
  );
2050
1585
  }
2051
1586
  const lockDir = `${this.ctx.getDbPath()}.lock.d`;
2052
- const lockFile = path4.join(lockDir, "lock.json");
1587
+ const lockFile = path2.join(lockDir, "lock.json");
2053
1588
  const nonce = randomUUID();
2054
1589
  try {
2055
1590
  fs2.mkdirSync(lockDir);
@@ -2190,7 +1725,10 @@ var BackupManager = class {
2190
1725
  } catch {
2191
1726
  }
2192
1727
  if (!rollbackSuccess) {
2193
- throw new Error(`CRITICAL: Database restore failed AND rollback failed. The database may be in an inconsistent state. Your previous database is preserved at: ${oldDbBackupPath}. Original error: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
1728
+ throw new Error(
1729
+ `CRITICAL: Database restore failed AND rollback failed. The database may be in an inconsistent state. Your previous database is preserved at: ${oldDbBackupPath}. Original error: ${error instanceof Error ? error.message : String(error)}`,
1730
+ { cause: error }
1731
+ );
2194
1732
  }
2195
1733
  await this.ctx.initialize();
2196
1734
  throw error;
@@ -2574,6 +2112,27 @@ var DatabaseAdapter2 = class {
2574
2112
  "DELETE FROM vec_embeddings WHERE entry_id NOT IN (SELECT id FROM memory_journal WHERE deleted_at IS NULL)"
2575
2113
  ).run();
2576
2114
  }
2115
+ pruneByImportance(olderThanDays, importanceThreshold) {
2116
+ const db = this.connection.getNativeDb();
2117
+ const cte = buildImportanceCte();
2118
+ const importanceExpr = buildImportanceSqlExpression();
2119
+ const deletedAt = (/* @__PURE__ */ new Date()).toISOString();
2120
+ const sql = `
2121
+ WITH ${cte}
2122
+ UPDATE memory_journal
2123
+ SET deleted_at = ?
2124
+ WHERE id IN (
2125
+ SELECT e.id
2126
+ FROM memory_journal e
2127
+ LEFT JOIN rel_stats rs ON e.id = rs.entry_id
2128
+ WHERE e.deleted_at IS NULL
2129
+ AND julianday('now') - julianday(e.timestamp) > ${String(olderThanDays)}
2130
+ AND ${importanceExpr} < ${String(importanceThreshold)}
2131
+ )
2132
+ `;
2133
+ const result = db.prepare(sql).run(deletedAt);
2134
+ return result.changes;
2135
+ }
2577
2136
  executeInTransaction(cb) {
2578
2137
  return this.connection.getNativeDb().transaction(cb)();
2579
2138
  }
@@ -3407,3832 +2966,41 @@ var VectorSearchManager = class {
3407
2966
  }
3408
2967
  };
3409
2968
 
3410
- // src/constants/icons.ts
3411
- var ICON_ANALYTICS = {
3412
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Cline x1="18" y1="20" x2="18" y2="10"/%3E%3Cline x1="12" y1="20" x2="12" y2="4"/%3E%3Cline x1="6" y1="20" x2="6" y2="14"/%3E%3C/svg%3E',
3413
- mimeType: "image/svg+xml",
3414
- sizes: ["24x24"]
3415
- };
3416
- var ICON_GITHUB = {
3417
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"%3E%3Cpath d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/%3E%3C/svg%3E',
3418
- mimeType: "image/svg+xml",
3419
- sizes: ["24x24"]
3420
- };
3421
- var ICON_TEAM = {
3422
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Cpath d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/%3E%3Ccircle cx="9" cy="7" r="4"/%3E%3Cpath d="M23 21v-2a4 4 0 00-3-3.87"/%3E%3Cpath d="M16 3.13a4 4 0 010 7.75"/%3E%3C/svg%3E',
3423
- mimeType: "image/svg+xml",
3424
- sizes: ["24x24"]
3425
- };
3426
- var ICON_BRIEFING = {
3427
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Cpath d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/%3E%3Cpolyline points="14 2 14 8 20 8"/%3E%3Cline x1="16" y1="13" x2="8" y2="13"/%3E%3Cline x1="16" y1="17" x2="8" y2="17"/%3E%3Cpolyline points="10 9 9 9 8 9"/%3E%3C/svg%3E',
3428
- mimeType: "image/svg+xml",
3429
- sizes: ["24x24"]
3430
- };
3431
- var ICON_GRAPH = {
3432
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Ccircle cx="18" cy="5" r="3"/%3E%3Ccircle cx="6" cy="12" r="3"/%3E%3Ccircle cx="18" cy="19" r="3"/%3E%3Cline x1="8.59" y1="13.51" x2="15.42" y2="17.49"/%3E%3Cline x1="15.41" y1="6.51" x2="8.59" y2="10.49"/%3E%3C/svg%3E',
3433
- mimeType: "image/svg+xml",
3434
- sizes: ["24x24"]
3435
- };
3436
- var ICON_CLOCK = {
3437
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Ccircle cx="12" cy="12" r="10"/%3E%3Cpolyline points="12 6 12 12 16 14"/%3E%3C/svg%3E',
3438
- mimeType: "image/svg+xml",
3439
- sizes: ["24x24"]
3440
- };
3441
- var ICON_HEALTH = {
3442
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Cpath d="M22 12h-4l-3 9L9 3l-3 9H2"/%3E%3C/svg%3E',
3443
- mimeType: "image/svg+xml",
3444
- sizes: ["24x24"]
3445
- };
3446
- var ICON_STAR = {
3447
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Cpolygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/%3E%3C/svg%3E',
3448
- mimeType: "image/svg+xml",
3449
- sizes: ["24x24"]
3450
- };
3451
- var ICON_TAG = {
3452
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Cpath d="M20.59 13.41l-7.17 7.17a2 2 0 01-2.83 0L2 12V2h10l8.59 8.59a2 2 0 010 2.82z"/%3E%3Cline x1="7" y1="7" x2="7.01" y2="7"/%3E%3C/svg%3E',
3453
- mimeType: "image/svg+xml",
3454
- sizes: ["24x24"]
3455
- };
3456
- var ICON_ISSUE = {
3457
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Ccircle cx="12" cy="12" r="10"/%3E%3Cline x1="12" y1="8" x2="12" y2="12"/%3E%3Cline x1="12" y1="16" x2="12.01" y2="16"/%3E%3C/svg%3E',
3458
- mimeType: "image/svg+xml",
3459
- sizes: ["24x24"]
3460
- };
3461
- var ICON_PR = {
3462
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Ccircle cx="18" cy="18" r="3"/%3E%3Ccircle cx="6" cy="6" r="3"/%3E%3Cpath d="M6 21V9a9 9 0 009 9"/%3E%3C/svg%3E',
3463
- mimeType: "image/svg+xml",
3464
- sizes: ["24x24"]
3465
- };
3466
- var ICON_MILESTONE = {
3467
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Cpath d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/%3E%3Cline x1="4" y1="22" x2="4" y2="15"/%3E%3C/svg%3E',
3468
- mimeType: "image/svg+xml",
3469
- sizes: ["24x24"]
3470
- };
3471
- var ICON_PROMPT = {
3472
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Cpath d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/%3E%3C/svg%3E',
3473
- mimeType: "image/svg+xml",
3474
- sizes: ["24x24"]
3475
- };
3476
- var ICON_FLAG = {
3477
- src: 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"%3E%3Cpath d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/%3E%3Cline x1="12" y1="9" x2="12" y2="13"/%3E%3Cline x1="12" y1="17" x2="12.01" y2="17"/%3E%3C/svg%3E',
3478
- mimeType: "image/svg+xml",
3479
- sizes: ["24x24"]
3480
- };
3481
-
3482
- // src/handlers/resources/core/briefing/github-section.ts
3483
- async function buildGitHubSection(github, config) {
3484
- if (!github) return null;
3485
- try {
3486
- const resolved = await resolveGitHubRepo(github, config);
3487
- if (isResourceError(resolved)) return null;
3488
- const { owner, repo } = resolved;
3489
- const batch1 = await Promise.allSettled([
3490
- fetchCiStatus(github, owner, repo, config),
3491
- fetchIssuesAndPrs(github, owner, repo, config),
3492
- fetchMilestones(github, owner, repo, config.milestoneCount ?? 3)
3493
- ]);
3494
- const batch2 = await Promise.allSettled([
3495
- fetchInsights(github, owner, repo),
3496
- config.copilotReviews ? fetchCopilotReviews(github, owner, repo) : Promise.resolve(void 0)
3497
- ]);
3498
- const [ciStatusResult, issuesAndPrsResult, milestonesResult] = batch1;
3499
- const [insightsResult, copilotReviewsResult] = batch2;
3500
- const degradedReasons = [];
3501
- const ciStatus = ciStatusResult.status === "fulfilled" ? ciStatusResult.value : { status: "unknown", workflowSummary: void 0, degraded: true };
3502
- if (ciStatusResult.status === "rejected")
3503
- degradedReasons.push(`CI Status fetch failed: ${String(ciStatusResult.reason)}`);
3504
- else if (ciStatus.degraded) degradedReasons.push("CI Status partially degraded");
3505
- const issuesAndPrs = issuesAndPrsResult.status === "fulfilled" ? issuesAndPrsResult.value : {
3506
- openIssues: 0,
3507
- openIssueList: void 0,
3508
- openPRs: 0,
3509
- openPrList: void 0,
3510
- degraded: true
3511
- };
3512
- if (issuesAndPrsResult.status === "rejected")
3513
- degradedReasons.push(`Issues/PRs fetch failed: ${String(issuesAndPrsResult.reason)}`);
3514
- else if (issuesAndPrs.degraded) degradedReasons.push("Issues/PRs partially degraded");
3515
- const milestonesData = milestonesResult.status === "fulfilled" ? milestonesResult.value : { items: [], degraded: true };
3516
- if (milestonesResult.status === "rejected")
3517
- degradedReasons.push(`Milestones fetch failed: ${String(milestonesResult.reason)}`);
3518
- else if (milestonesData.degraded) degradedReasons.push("Milestones partially degraded");
3519
- const insightsData = insightsResult.status === "fulfilled" ? insightsResult.value : { degraded: true };
3520
- if (insightsResult.status === "rejected")
3521
- degradedReasons.push(`Insights fetch failed: ${String(insightsResult.reason)}`);
3522
- else if (insightsData.degraded) degradedReasons.push("Insights partially degraded");
3523
- const copilotReviewsData = copilotReviewsResult.status === "fulfilled" ? copilotReviewsResult.value ?? {} : { degraded: true };
3524
- if (copilotReviewsResult.status === "rejected")
3525
- degradedReasons.push(
3526
- `Copilot Reviews fetch failed: ${String(copilotReviewsResult.reason)}`
3527
- );
3528
- else if (copilotReviewsData.degraded)
3529
- degradedReasons.push("Copilot Reviews partially degraded");
3530
- const { openIssues, openIssueList, openPRs, openPrList } = issuesAndPrs;
3531
- const workflowSummary = ciStatus.workflowSummary;
3532
- const milestones = milestonesData.items ?? [];
3533
- const insights = insightsData.insights;
3534
- const copilotReviews = copilotReviewsData.reviews;
3535
- const degraded = degradedReasons.length > 0;
3536
- return {
3537
- repo: `${owner}/${repo}`,
3538
- branch: resolved.branch,
3539
- ci: ciStatus.status,
3540
- openIssues,
3541
- openPRs,
3542
- milestones,
3543
- insights,
3544
- openIssueList,
3545
- openPrList,
3546
- ...config.prStatusBreakdown && openPrList ? {
3547
- prStatusSummary: {
3548
- open: openPrList.filter((p) => p.state === "OPEN").length + (openPRs - (openPrList?.filter((p) => p.state === "OPEN").length ?? 0)),
3549
- merged: openPrList.filter((p) => p.state === "MERGED").length,
3550
- closed: openPrList.filter((p) => p.state === "CLOSED").length
3551
- }
3552
- } : {},
3553
- ...workflowSummary ? { workflowSummary } : {},
3554
- ...copilotReviews ? { copilotReviews } : {},
3555
- ...degraded ? { degraded: true, degradedReasons } : {}
3556
- };
3557
- } catch (error) {
3558
- logger.debug("Failed to build GitHub briefing section", {
3559
- module: "BRIEFING",
3560
- operation: "github-section",
3561
- error
3562
- });
3563
- return null;
2969
+ // src/server/scheduler.ts
2970
+ var Scheduler = class {
2971
+ options;
2972
+ db;
2973
+ vectorManager;
2974
+ timers = [];
2975
+ started = false;
2976
+ runtime;
2977
+ constructor(options, db, vectorManager, runtime) {
2978
+ this.options = options;
2979
+ this.db = db;
2980
+ this.vectorManager = vectorManager ?? null;
2981
+ this.runtime = runtime;
3564
2982
  }
3565
- }
3566
- async function fetchCiStatus(github, owner, repo, config) {
3567
- try {
3568
- const runLimit = Math.max(1, config.workflowCount, config.workflowStatusBreakdown ? 10 : 1);
3569
- const runs = await github.getWorkflowRuns(owner, repo, runLimit);
3570
- if (runs.length === 0) return { status: "unknown" };
3571
- const primaryRun = runs.find(
3572
- (r) => r.status !== "completed" || ["success", "failure", "cancelled"].includes(r.conclusion ?? "")
3573
- ) ?? runs[0];
3574
- const latestRun = primaryRun;
3575
- let status;
3576
- if (!latestRun) {
3577
- status = "unknown";
3578
- } else if (latestRun.status !== "completed") {
3579
- status = "pending";
3580
- } else {
3581
- switch (latestRun.conclusion) {
3582
- case "success":
3583
- status = "passing";
3584
- break;
3585
- case "failure":
3586
- status = "failing";
3587
- break;
3588
- case "cancelled":
3589
- status = "cancelled";
3590
- break;
3591
- default:
3592
- status = "unknown";
3593
- }
2983
+ /**
2984
+ * Start all enabled scheduled jobs.
2985
+ * Each job runs on its own interval and failures are isolated.
2986
+ */
2987
+ start() {
2988
+ if (this.started) {
2989
+ logger.warning("Scheduler already started, ignoring duplicate start()", {
2990
+ module: "Scheduler"
2991
+ });
2992
+ return;
3594
2993
  }
3595
- let workflowSummary = void 0;
3596
- if (config.workflowStatusBreakdown || config.workflowCount > 0) {
3597
- const counts = { passing: 0, failing: 0, pending: 0, cancelled: 0 };
3598
- for (const run of runs) {
3599
- if (run.status !== "completed") {
3600
- counts.pending++;
3601
- } else {
3602
- switch (run.conclusion) {
3603
- case "success":
3604
- counts.passing++;
3605
- break;
3606
- case "failure":
3607
- counts.failing++;
3608
- break;
3609
- case "cancelled":
3610
- counts.cancelled++;
3611
- break;
3612
- default:
3613
- break;
3614
- }
3615
- }
3616
- }
3617
- workflowSummary = {
3618
- ...counts,
3619
- ...config.workflowCount > 0 ? {
3620
- runs: runs.slice(0, config.workflowCount).map((r) => ({
3621
- name: r.name,
3622
- conclusion: r.status !== "completed" ? "pending" : r.conclusion ?? "unknown"
3623
- }))
3624
- } : {}
3625
- };
2994
+ this.started = true;
2995
+ process.on("SIGTERM", () => this.stop());
2996
+ const { backupIntervalMinutes, vacuumIntervalMinutes, rebuildIndexIntervalMinutes } = this.options;
2997
+ if (backupIntervalMinutes > 0) {
2998
+ this.scheduleJob("backup", backupIntervalMinutes, () => this.runBackup());
3626
2999
  }
3627
- return { status, workflowSummary };
3628
- } catch (error) {
3629
- logger.debug("Failed to fetch CI status", {
3630
- module: "BRIEFING",
3631
- operation: "ci-status",
3632
- error: error instanceof Error ? error.message : String(error)
3633
- });
3634
- return { status: "unknown", degraded: true };
3635
- }
3636
- }
3637
- async function fetchIssuesAndPrs(github, owner, repo, config) {
3638
- try {
3639
- const issueLimit = Math.max(1, config.issueCount || 1);
3640
- const issues = await github.getIssues(owner, repo, "open", issueLimit);
3641
- const openIssues = issues.length > 0 ? issues.length : 0;
3642
- const openIssueList = config.issueCount > 0 && issues.length > 0 ? issues.slice(0, config.issueCount).map((i) => ({
3643
- number: i.number,
3644
- title: markUntrustedContentInline(i.title)
3645
- })) : void 0;
3646
- const prState = config.prStatusBreakdown ? "all" : "open";
3647
- const prLimit = config.prStatusBreakdown ? Math.max(20, config.prCount) : Math.max(1, config.prCount || 1);
3648
- const prs = await github.getPullRequests(owner, repo, prState, prLimit);
3649
- const openPRs = config.prStatusBreakdown ? prs.filter((p) => p.state === "OPEN").length : prs.length > 0 ? prs.length : 0;
3650
- const openPrList = config.prCount > 0 && prs.length > 0 ? prs.slice(0, config.prCount).map((p) => ({
3651
- number: p.number,
3652
- title: markUntrustedContentInline(p.title),
3653
- state: p.state
3654
- })) : void 0;
3655
- return { openIssues, openPRs, openIssueList, openPrList };
3656
- } catch (error) {
3657
- logger.debug("Failed to fetch issues and PRs", {
3658
- module: "BRIEFING",
3659
- operation: "issues-prs",
3660
- error: error instanceof Error ? error.message : String(error)
3661
- });
3662
- return { openIssues: 0, openPRs: 0, degraded: true };
3663
- }
3664
- }
3665
- async function fetchMilestones(github, owner, repo, limit) {
3666
- if (limit <= 0) return { items: [] };
3667
- try {
3668
- const msList = await github.getMilestones(owner, repo, "open", limit);
3669
- return {
3670
- items: msList.map((m) => {
3671
- const pct = milestoneCompletionPct(m.openIssues, m.closedIssues);
3672
- return {
3673
- title: m.title,
3674
- progress: `${String(pct)}%`,
3675
- dueOn: m.dueOn
3676
- };
3677
- })
3678
- };
3679
- } catch (error) {
3680
- logger.debug("Failed to fetch milestones", {
3681
- module: "BRIEFING",
3682
- operation: "milestones",
3683
- error: error instanceof Error ? error.message : String(error)
3684
- });
3685
- return { items: [], degraded: true };
3686
- }
3687
- }
3688
- async function fetchInsights(github, owner, repo) {
3689
- try {
3690
- const repoStats = await github.getRepoStats(owner, repo);
3691
- if (!repoStats) return { degraded: true };
3692
- const result = {
3693
- stars: repoStats.stars ?? null,
3694
- forks: repoStats.forks ?? null
3695
- };
3696
- try {
3697
- const trafficData = await github.getTrafficData(owner, repo);
3698
- if (trafficData) {
3699
- result.clones14d = trafficData.clones.total;
3700
- result.views14d = trafficData.views.total;
3701
- }
3702
- } catch (error) {
3703
- logger.debug("Traffic data unavailable (requires push access)", {
3704
- module: "BRIEFING",
3705
- operation: "traffic",
3706
- error: error instanceof Error ? error.message : String(error)
3707
- });
3708
- return { insights: result, degraded: true };
3709
- }
3710
- return { insights: result };
3711
- } catch (error) {
3712
- logger.debug("Failed to fetch repo insights", {
3713
- module: "BRIEFING",
3714
- operation: "insights",
3715
- error: error instanceof Error ? error.message : String(error)
3716
- });
3717
- return { degraded: true };
3718
- }
3719
- }
3720
- async function fetchCopilotReviews(github, owner, repo) {
3721
- try {
3722
- const recentPrs = await github.getPullRequests(owner, repo, "all", 10);
3723
- let reviewed = 0;
3724
- let approved = 0;
3725
- let changesRequested = 0;
3726
- let totalComments = 0;
3727
- const summaries = [];
3728
- for (const pr of recentPrs.slice(0, 5)) {
3729
- summaries.push(await github.getCopilotReviewSummary(owner, repo, pr.number));
3730
- }
3731
- for (const summary of summaries) {
3732
- if (summary.state !== "none") {
3733
- reviewed++;
3734
- if (summary.state === "approved") approved++;
3735
- else if (summary.state === "changes_requested") changesRequested++;
3736
- totalComments += summary.commentCount;
3737
- }
3738
- }
3739
- return reviewed > 0 ? { reviews: { reviewed, approved, changesRequested, totalComments } } : void 0;
3740
- } catch (error) {
3741
- logger.debug("Failed to fetch Copilot reviews", {
3742
- module: "BRIEFING",
3743
- operation: "copilot-reviews",
3744
- error
3745
- });
3746
- return { degraded: true };
3747
- }
3748
- }
3749
- var PREVIEW_LENGTH2 = 80;
3750
- function buildJournalContext(context, config, projectNumber) {
3751
- const recentEntries = typeof projectNumber === "number" ? context.db.searchEntries("", { limit: config.entryCount, projectNumber }) : context.db.getRecentEntries(config.entryCount);
3752
- const latestEntries = recentEntries.map((e) => {
3753
- const content = e.content ?? "";
3754
- return {
3755
- id: e.id,
3756
- timestamp: e.timestamp,
3757
- type: e.entryType,
3758
- preview: markUntrustedContentInline(
3759
- content.slice(0, PREVIEW_LENGTH2) + (content.length > PREVIEW_LENGTH2 ? "..." : "")
3760
- )
3761
- };
3762
- });
3763
- const summaryEntries = typeof projectNumber === "number" ? context.db.searchEntries("", {
3764
- limit: config.summaryCount,
3765
- projectNumber,
3766
- tags: ["session-summary"]
3767
- }) : context.db.searchEntries("", {
3768
- limit: config.summaryCount,
3769
- tags: ["session-summary"]
3770
- });
3771
- const retroEntries = summaryEntries.length === 0 ? typeof projectNumber === "number" ? context.db.searchEntries("", {
3772
- limit: config.summaryCount,
3773
- projectNumber,
3774
- entryType: "retrospective"
3775
- }) : context.db.searchEntries("", {
3776
- limit: config.summaryCount,
3777
- entryType: "retrospective"
3778
- }) : [];
3779
- const finalSummaryEntries = summaryEntries.length > 0 ? summaryEntries : retroEntries;
3780
- let latestSessionSummary;
3781
- let sessionSummaries;
3782
- if (finalSummaryEntries.length > 0) {
3783
- sessionSummaries = finalSummaryEntries.map((entry) => {
3784
- const c = entry.content ?? "";
3785
- return {
3786
- id: entry.id,
3787
- timestamp: entry.timestamp,
3788
- type: entry.entryType,
3789
- preview: markUntrustedContentInline(
3790
- c.slice(0, PREVIEW_LENGTH2) + (c.length > PREVIEW_LENGTH2 ? "..." : "")
3791
- )
3792
- };
3793
- });
3794
- latestSessionSummary = sessionSummaries[0];
3795
- }
3796
- const totalEntries = context.db.getActiveEntryCount();
3797
- const lastModified = recentEntries[0]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
3798
- return { totalEntries, latestEntries, latestSessionSummary, sessionSummaries, lastModified };
3799
- }
3800
- var TEAM_PREVIEW_LENGTH = 60;
3801
- function buildTeamContext(context, config, projectNumber) {
3802
- if (!context.teamDb) return void 0;
3803
- try {
3804
- const teamTotalEntries = context.teamDb.getActiveEntryCount();
3805
- const teamRecent = typeof projectNumber === "number" ? context.teamDb.searchEntries("", { limit: 1, projectNumber }) : context.teamDb.getRecentEntries(1);
3806
- const teamLatestEntry = teamRecent[0];
3807
- const teamContent = teamLatestEntry ? teamLatestEntry["content"] ?? "" : "";
3808
- const teamLatest = teamLatestEntry ? `#${String(teamLatestEntry["id"])}: ${markUntrustedContentInline(teamContent.slice(0, TEAM_PREVIEW_LENGTH) + (teamContent.length > TEAM_PREVIEW_LENGTH ? "..." : ""))}` : null;
3809
- const teamInfo = {
3810
- totalEntries: teamTotalEntries,
3811
- latestPreview: teamLatest
3812
- };
3813
- let teamLatestEntries = void 0;
3814
- if (config.includeTeam) {
3815
- const teamEntries = typeof projectNumber === "number" ? context.teamDb.searchEntries("", { limit: config.entryCount, projectNumber }) : context.teamDb.getRecentEntries(config.entryCount);
3816
- teamLatestEntries = teamEntries.map((e) => {
3817
- const content = e.content ?? "";
3818
- return {
3819
- id: e.id,
3820
- timestamp: e.timestamp,
3821
- type: e.entryType,
3822
- preview: markUntrustedContentInline(
3823
- content.slice(0, TEAM_PREVIEW_LENGTH) + (content.length > TEAM_PREVIEW_LENGTH ? "..." : "")
3824
- )
3825
- };
3826
- });
3827
- }
3828
- return { teamInfo, teamLatestEntries };
3829
- } catch (error) {
3830
- logger.debug("Failed to build team context", {
3831
- module: "BRIEFING",
3832
- operation: "team-context",
3833
- error: error instanceof Error ? error.message : String(error)
3834
- });
3835
- return void 0;
3836
- }
3837
- }
3838
- var MS_PER_HOUR = 36e5;
3839
- var MS_PER_DAY = 864e5;
3840
- function buildRulesFileInfo(rulesFilePath, allowedIoRoots = []) {
3841
- if (!rulesFilePath) return void 0;
3842
- try {
3843
- const expandedRoots = [...allowedIoRoots, path4.dirname(rulesFilePath)];
3844
- assertSafeFilePath(rulesFilePath, expandedRoots);
3845
- const stat2 = fs2.statSync(rulesFilePath);
3846
- const ageMs = Date.now() - stat2.mtimeMs;
3847
- const ageHours = Math.floor(ageMs / MS_PER_HOUR);
3848
- const ageDays = Math.floor(ageMs / MS_PER_DAY);
3849
- const agoStr = ageDays > 0 ? `${String(ageDays)}d ago` : ageHours > 0 ? `${String(ageHours)}h ago` : "just now";
3850
- return {
3851
- path: path4.basename(rulesFilePath),
3852
- name: path4.basename(rulesFilePath),
3853
- sizeKB: Math.round(stat2.size / 1024),
3854
- lastModified: agoStr
3855
- };
3856
- } catch (error) {
3857
- logger.debug("Failed to read rules file", {
3858
- module: "BRIEFING",
3859
- operation: "rules-file",
3860
- error: error instanceof Error ? error.message : String(error)
3861
- });
3862
- return void 0;
3863
- }
3864
- }
3865
- function buildSkillsDirInfo(skillsDirPath, allowedIoRoots = []) {
3866
- if (!skillsDirPath) return void 0;
3867
- try {
3868
- const expandedRoots = [...allowedIoRoots, skillsDirPath];
3869
- assertSafeDirectoryPath(skillsDirPath, expandedRoots);
3870
- const entries = fs2.readdirSync(skillsDirPath, { withFileTypes: true });
3871
- const skillDirs = entries.filter((e) => e.isDirectory());
3872
- return {
3873
- path: path4.basename(skillsDirPath),
3874
- count: skillDirs.length,
3875
- names: skillDirs.map((d) => d.name)
3876
- };
3877
- } catch (error) {
3878
- logger.debug("Failed to read skills directory", {
3879
- module: "BRIEFING",
3880
- operation: "skills-dir",
3881
- error: error instanceof Error ? error.message : String(error)
3882
- });
3883
- return void 0;
3884
- }
3885
- }
3886
- function buildFlagsContext(context) {
3887
- if (!context.teamDb) return void 0;
3888
- try {
3889
- const flagEntries = context.teamDb.searchEntries("", {
3890
- entryType: "flag",
3891
- limit: 20
3892
- });
3893
- const activeFlags = flagEntries.map((entry) => {
3894
- const ctx = parseFlagContext(entry.autoContext);
3895
- if (!ctx || ctx.resolved) return null;
3896
- const content = entry.content ?? "";
3897
- return {
3898
- id: entry.id,
3899
- flag_type: ctx.flag_type,
3900
- target_user: ctx.target_user ?? null,
3901
- preview: markUntrustedContentInline(
3902
- content.slice(0, 80) + (content.length > 80 ? "..." : "")
3903
- ),
3904
- timestamp: entry.timestamp
3905
- };
3906
- }).filter((f) => f !== null);
3907
- if (activeFlags.length === 0) return void 0;
3908
- return {
3909
- count: activeFlags.length,
3910
- flags: activeFlags
3911
- };
3912
- } catch (error) {
3913
- logger.debug("Failed to build flags context", {
3914
- module: "BRIEFING",
3915
- operation: "flags-context",
3916
- error: error instanceof Error ? error.message : String(error)
3917
- });
3918
- return void 0;
3919
- }
3920
- }
3921
-
3922
- // src/handlers/resources/core/briefing/user-message.ts
3923
- var escapeTableCell = (text) => text.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r?\n/g, "<br>");
3924
- function formatUserMessage(opts) {
3925
- const {
3926
- repoName,
3927
- branchName,
3928
- totalEntries,
3929
- latestPreview,
3930
- summaryPreviews,
3931
- github,
3932
- rulesFile,
3933
- skillsDir,
3934
- analyticsInsights
3935
- } = opts;
3936
- let ciDisplay = opts.ciStatus;
3937
- if (github?.workflowSummary) {
3938
- const ws = github.workflowSummary;
3939
- if (ws.runs && ws.runs.length > 0) {
3940
- const icons = {
3941
- success: "\u2705",
3942
- failure: "\u274C",
3943
- pending: "\u23F3",
3944
- cancelled: "\u26D4",
3945
- unknown: "\u2753"
3946
- };
3947
- ciDisplay = ws.runs.map((r) => `${icons[r.conclusion] ?? "\u2753"} ${r.name}`).join(" \xB7 ");
3948
- } else {
3949
- const parts = [];
3950
- if (ws.passing > 0) parts.push(`${String(ws.passing)} passing`);
3951
- if (ws.failing > 0) parts.push(`${String(ws.failing)} failing`);
3952
- if (ws.pending > 0) parts.push(`${String(ws.pending)} pending`);
3953
- if (ws.cancelled > 0) parts.push(`${String(ws.cancelled)} cancelled`);
3954
- ciDisplay = parts.join(" \xB7 ") || opts.ciStatus;
3955
- }
3956
- }
3957
- let issuesRow = "";
3958
- let prsRow = "";
3959
- if (github) {
3960
- if (github.openIssueList && github.openIssueList.length > 0) {
3961
- const titles = github.openIssueList.map((i) => `#${String(i.number)} ${i.title}`).join(" \xB7 ");
3962
- issuesRow = `
3963
- | **Issues** | ${String(github.openIssues)} open: ${escapeTableCell(titles)} |`;
3964
- } else {
3965
- issuesRow = `
3966
- | **Issues** | ${String(github.openIssues)} open |`;
3967
- }
3968
- if (github.prStatusSummary) {
3969
- const s = github.prStatusSummary;
3970
- const parts = [];
3971
- if (s.open > 0) parts.push(`${String(s.open)} open`);
3972
- if (s.merged > 0) parts.push(`${String(s.merged)} merged`);
3973
- if (s.closed > 0) parts.push(`${String(s.closed)} closed`);
3974
- prsRow = `
3975
- | **PRs** | ${parts.join(" \xB7 ") || "0"} |`;
3976
- } else if (github.openPrList && github.openPrList.length > 0) {
3977
- const titles = github.openPrList.map((p) => `#${String(p.number)} ${p.title}`).join(" \xB7 ");
3978
- prsRow = `
3979
- | **PRs** | ${String(github.openPRs)} open: ${escapeTableCell(titles)} |`;
3980
- } else {
3981
- prsRow = `
3982
- | **PRs** | ${String(github.openPRs)} open |`;
3983
- }
3984
- }
3985
- const milestoneRow = github?.milestones && github.milestones.length > 0 ? `
3986
- | **Milestones** | ${escapeTableCell(github.milestones.map((m) => `${m.title} (${m.progress}${m.dueOn ? `, due ${m.dueOn.split("T")[0] ?? ""}` : ""})`).join(", "))} |` : "";
3987
- let insightsRow = "";
3988
- if (github?.insights) {
3989
- const parts = [];
3990
- if (github.insights.stars !== null) parts.push(`\u2B50 ${String(github.insights.stars)} stars`);
3991
- if (github.insights.forks !== null) parts.push(`\u{1F374} ${String(github.insights.forks)} forks`);
3992
- if (github.insights.clones14d !== void 0)
3993
- parts.push(`\u{1F4E6} ${String(github.insights.clones14d)} clones`);
3994
- if (github.insights.views14d !== void 0)
3995
- parts.push(`\u{1F441}\uFE0F ${String(github.insights.views14d)} views`);
3996
- if (parts.length > 0) {
3997
- const trafficNote = github.insights.clones14d !== void 0 ? " (14d)" : "";
3998
- insightsRow = `
3999
- | **Insights** | ${parts.join(" \xB7 ")}${trafficNote} |`;
4000
- }
4001
- }
4002
- const copilotRow = github?.copilotReviews ? `
4003
- | **Copilot** | ${String(github.copilotReviews.reviewed)} reviewed \xB7 ${String(github.copilotReviews.approved)} approved${github.copilotReviews.changesRequested > 0 ? ` \xB7 ${String(github.copilotReviews.changesRequested)} changes requested` : ""}${github.copilotReviews.totalComments > 0 ? ` (${String(github.copilotReviews.totalComments)} comments)` : ""} |` : "";
4004
- let analyticsRow = "";
4005
- if (analyticsInsights) {
4006
- const parts = [];
4007
- parts.push(`\u{1F4C8} ${analyticsInsights.activityTrend}`);
4008
- if (analyticsInsights.significanceSpike !== null)
4009
- parts.push(`\u{1F525} ${analyticsInsights.significanceSpike}`);
4010
- if (analyticsInsights.relationshipDensity !== void 0)
4011
- parts.push(`\u{1F517} Relationship density: ${analyticsInsights.relationshipDensity}`);
4012
- if (analyticsInsights.staleProjects.length > 0)
4013
- parts.push(`\u{1F4A4} ${analyticsInsights.staleProjects.length} stale projects`);
4014
- analyticsRow = `
4015
- | **Analytics** | ${escapeTableCell(parts.join(" \xB7 "))} |`;
4016
- }
4017
- const summariesOutput = summaryPreviews && summaryPreviews.length > 0 ? summaryPreviews.map((s) => `
4018
- | **Summary** | ${escapeTableCell(s)} |`).join("") : "";
4019
- let flagsRow = "";
4020
- if (opts.flagSummary && opts.flagSummary.count > 0) {
4021
- const flagParts = opts.flagSummary.flags.slice(0, 5).map((f) => {
4022
- const target = f.target_user ? ` \u2192 @${f.target_user}` : "";
4023
- return `\u{1F6A9} ${f.flag_type}${target}: ${f.preview.slice(0, 50)}`;
4024
- });
4025
- flagsRow = `
4026
- | **Flags** | ${escapeTableCell(flagParts.join(" \xB7 "))}${opts.flagSummary.count > 5 ? ` (+${String(opts.flagSummary.count - 5)} more)` : ""} |`;
4027
- }
4028
- return `\u{1F4CB} **Session Context Loaded**
4029
- | Context | Value |
4030
- |---------|-------|
4031
- | **Project** | ${escapeTableCell(repoName)} |
4032
- | **Branch** | ${escapeTableCell(branchName)} |
4033
- | **CI** | ${escapeTableCell(ciDisplay)} |
4034
- | **Journal** | ${totalEntries} entries |${opts.teamTotalEntries !== void 0 ? `
4035
- | **Team DB** | ${opts.teamTotalEntries} entries |` : ""}
4036
- | **Latest** | ${escapeTableCell(latestPreview)} |${summariesOutput}${issuesRow}${prsRow}${milestoneRow}${insightsRow}${copilotRow}${analyticsRow}${flagsRow}${rulesFile ? `
4037
- | **Rules** | ${escapeTableCell(rulesFile.name)} (${String(rulesFile.sizeKB)} KB, updated ${rulesFile.lastModified}) |` : ""}${skillsDir ? `
4038
- | **Skills** | ${String(skillsDir.count)} skill${skillsDir.count !== 1 ? "s" : ""} available |` : ""}`;
4039
- }
4040
-
4041
- // src/handlers/resources/core/briefing/insights-section.ts
4042
- function buildInsightsSection(context) {
4043
- const snapshot = resolveDigestSnapshot(context);
4044
- if (!snapshot) return null;
4045
- return formatDigest(snapshot);
4046
- }
4047
- function resolveDigestSnapshot(context) {
4048
- const schedulerDigest = context.scheduler?.getLatestDigest?.();
4049
- if (schedulerDigest) return schedulerDigest;
4050
- const dbSnapshot = context.db?.getLatestAnalyticsSnapshot?.("digest");
4051
- if (dbSnapshot) return dbSnapshot.data;
4052
- return null;
4053
- }
4054
- function formatDigest(snapshot) {
4055
- let activityTrend;
4056
- if (snapshot.activityGrowthPercent !== null) {
4057
- const sign = snapshot.activityGrowthPercent >= 0 ? "+" : "";
4058
- activityTrend = `${sign}${String(snapshot.activityGrowthPercent)}% vs. last period (${String(snapshot.currentPeriodEntries)} entries)`;
4059
- } else {
4060
- activityTrend = `${String(snapshot.currentPeriodEntries)} entries this period (no previous data)`;
4061
- }
4062
- let significanceSpike = null;
4063
- if (snapshot.currentPeriodSignificant > 0) {
4064
- if (snapshot.significanceMultiplier !== null && snapshot.significanceMultiplier > 1.5) {
4065
- significanceSpike = `${String(snapshot.currentPeriodSignificant)} significant entries (${String(snapshot.significanceMultiplier)}\xD7 avg)`;
4066
- } else {
4067
- significanceSpike = `${String(snapshot.currentPeriodSignificant)} significant entries this period`;
4068
- }
4069
- }
4070
- return {
4071
- activityTrend,
4072
- significanceSpike,
4073
- staleProjects: snapshot.staleProjects.map((p) => ({
4074
- projectNumber: p.projectNumber,
4075
- daysSilent: p.daysSilent
4076
- })),
4077
- topImportance: snapshot.topImportanceEntries.map((e) => ({
4078
- id: e.id,
4079
- score: e.score,
4080
- preview: e.preview
4081
- })),
4082
- ...snapshot.currentRelDensity > 0 ? { relationshipDensity: snapshot.currentRelDensity } : {}
4083
- };
4084
- }
4085
-
4086
- // src/handlers/resources/core/briefing/index.ts
4087
- var briefingResource = {
4088
- uri: "memory://briefing",
4089
- name: "Initial Briefing",
4090
- title: "Session Initialization Context",
4091
- description: "AUTO-READ AT SESSION START: Project context for AI agents (~300 tokens). Contains userMessage to show user.",
4092
- mimeType: "application/json",
4093
- icons: [ICON_BRIEFING],
4094
- annotations: {
4095
- ...withSessionInit(withPriority(1, ASSISTANT_FOCUSED)),
4096
- autoRead: true
4097
- },
4098
- handler: async (_uri, context) => {
4099
- return buildBriefingData(context);
4100
- }
4101
- };
4102
- var dynamicBriefingResource = {
4103
- uri: "memory://briefing/{+repo}",
4104
- name: "Dynamic Briefing",
4105
- title: "Project-Specific Session Context",
4106
- description: "Project-specific briefing context for AI agents. Same as memory://briefing but targets a specific repository name from the registered workspaces.",
4107
- mimeType: "application/json",
4108
- icons: [ICON_BRIEFING],
4109
- annotations: {
4110
- ...withPriority(0.8, ASSISTANT_FOCUSED)
4111
- },
4112
- handler: async (uri, context) => {
4113
- const match = /memory:\/\/briefing\/(.+)/.exec(uri);
4114
- const repoName = match?.[1] ? decodeURIComponent(match[1]) : void 0;
4115
- return buildBriefingData(context, repoName);
4116
- }
4117
- };
4118
- async function buildBriefingData(context, targetRepo) {
4119
- const config = { ...DEFAULT_BRIEFING_CONFIG, ...context.briefingConfig };
4120
- let activeGithub = context.github;
4121
- let activeProjectNumber = config.defaultProjectNumber;
4122
- if (targetRepo && config.projectRegistry?.[targetRepo]) {
4123
- const repoPath = config.projectRegistry[targetRepo].path;
4124
- activeGithub = getGitHubIntegration(repoPath, context.runtime);
4125
- activeProjectNumber = config.projectRegistry[targetRepo].project_number ?? void 0;
4126
- }
4127
- const journal = buildJournalContext(context, config, activeProjectNumber);
4128
- const github = await buildGitHubSection(activeGithub, config);
4129
- const team = buildTeamContext(context, config, activeProjectNumber);
4130
- const rulesFile = buildRulesFileInfo(config.rulesFilePath, config.allowedIoRoots);
4131
- const skillsDir = buildSkillsDirInfo(config.skillsDirPath, config.allowedIoRoots);
4132
- const insights = buildInsightsSection(context);
4133
- const flags = buildFlagsContext(context);
4134
- const latestPreview = journal.latestEntries[0] ? `#${journal.latestEntries[0].id} (${journal.latestEntries[0].type}): ${journal.latestEntries[0].preview}` : "No entries yet";
4135
- const summaryPreviews = journal.sessionSummaries ? journal.sessionSummaries.map((s) => `#${s.id} (${s.type}): ${s.preview}`) : null;
4136
- const userMessage = formatUserMessage({
4137
- repoName: github?.repo ?? "local",
4138
- branchName: github?.branch ?? "unknown",
4139
- ciStatus: github?.ci ?? "unknown",
4140
- totalEntries: journal.totalEntries,
4141
- latestPreview,
4142
- summaryPreviews,
4143
- github,
4144
- teamTotalEntries: team?.teamInfo.totalEntries,
4145
- rulesFile,
4146
- skillsDir,
4147
- analyticsInsights: insights ?? void 0,
4148
- flagSummary: flags
4149
- });
4150
- return {
4151
- data: {
4152
- version: VERSION,
4153
- serverTime: (/* @__PURE__ */ new Date()).toISOString(),
4154
- localTime: new Intl.DateTimeFormat("en-US", {
4155
- dateStyle: "full",
4156
- timeStyle: "short",
4157
- timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
4158
- }).format(/* @__PURE__ */ new Date()),
4159
- journal: {
4160
- totalEntries: journal.totalEntries,
4161
- latestEntries: journal.latestEntries,
4162
- ...journal.latestSessionSummary ? { latestSessionSummary: journal.latestSessionSummary } : {}
4163
- },
4164
- github,
4165
- teamContext: team?.teamInfo,
4166
- ...team?.teamLatestEntries ? { teamLatestEntries: team.teamLatestEntries } : {},
4167
- ...rulesFile ? { rulesFile } : {},
4168
- ...skillsDir ? { skillsDir } : {},
4169
- ...insights ? { insights } : {},
4170
- ...flags ? { activeFlags: flags } : {},
4171
- ...config.projectRegistry ? {
4172
- registeredWorkspaces: Object.fromEntries(
4173
- Object.entries(config.projectRegistry).map(([k, v]) => {
4174
- const strippedPath = v.path.split(/[\\/]/).pop() || v.path;
4175
- return [k, { ...v, path: strippedPath }];
4176
- })
4177
- )
4178
- } : {},
4179
- behaviors: {
4180
- create: "implementations, decisions, bug-fixes, milestones",
4181
- search: "before decisions, referencing prior work",
4182
- link: "implementation\u2192spec, bugfix\u2192issue"
4183
- },
4184
- templateResources: [
4185
- "memory://github/status/{repo}",
4186
- "memory://github/insights/{repo}",
4187
- "memory://github/milestones/{repo}",
4188
- "memory://milestones/{repo}/{number}",
4189
- "memory://projects/{number}/timeline",
4190
- "memory://issues/{issue_number}/entries",
4191
- "memory://prs/{pr_number}/entries",
4192
- "memory://prs/{pr_number}/timeline",
4193
- "memory://kanban/{project_number}",
4194
- "memory://kanban/{project_number}/diagram",
4195
- "memory://milestones/{number}"
4196
- ],
4197
- more: {
4198
- fullHealth: "memory://health",
4199
- allRecent: "memory://recent",
4200
- githubStatus: "memory://github/status",
4201
- repoInsights: "memory://github/insights",
4202
- contextBundle: "get-context-bundle prompt"
4203
- },
4204
- userMessage,
4205
- clientNote: "For full tool reference and field notes, read memory://instructions \u2014 only if your client did NOT auto-inject server instructions at session start (most modern clients including AntiGravity do this automatically).\\n" + (config.projectRegistry ? "\\nMulti-project registry detected. To retrieve CI status, branch, and issues for a specific project, use the get_github_context tool or dynamic resources (e.g. memory://github/status/{repo}) with the repository name." : "")
4206
- },
4207
- annotations: { lastModified: journal.lastModified }
4208
- };
4209
- }
4210
-
4211
- // src/constants/server-instructions.ts
4212
- var CORE_INSTRUCTIONS = `# memory-journal-mcp
4213
-
4214
- ## **ESSENTIAL SESSION START!**
4215
-
4216
- 1. You **MUST** read the \`memory://briefing/{repo_name}\` at the start of each chat!
4217
- 2. Use the standard MCP \`read_resource\` tool for this (do NOT use Code Mode/execute_code).
4218
- 3. Infer the \`repo_name\` from the user's prompt or your active workspace context.
4219
- 4. **ACKNOWLEDGE FLAGS**: If the briefing JSON contains \`activeFlags\` (count > 0), you MUST print an alert ABOVE the table: \`\u26A0\uFE0F **{count} active flag(s)** \u2014 review before proceeding.\` followed by each flag (\`\u{1F6A9} {flag_type} \u2192 @{target_user}: {preview}\`).
4220
- 5. **RENDER TABLE**: Parse the remaining JSON into a dense 2-column Markdown Table (Field, Value).
4221
- - **RESTRICTION**: NO bulleted lists inside the table. Do NOT truncate summaries or issues.
4222
- - **FORMATTING**: Group related properties (use \`<br>\` for line breaks).
4223
- - **REQUIRED GROUPS**: GitHub, Issues, Entry Counts, Latest Entries/Summaries, Analytics, Milestones, Workspaces.
4224
- 6. **STOP & WAIT**: Do NOT autonomously resume past tasks or start work on new issues. The briefing is strictly for context.
4225
-
4226
- - **AntiGravity**: Tools are \`mcp_{name}_{tool}\` \u2192 server name = \`memory-journal-mcp\`
4227
- - **Cursor**: Tools are \`user-{name}-{tool}\` \u2192 server name = \`user-memory-journal-mcp\`
4228
- - **Other clients**: Use configured name exactly. Use tool-prefix discovery if unsure.
4229
-
4230
- ## Behaviors
4231
-
4232
- ### memory-journal-mcp Behaviors
4233
-
4234
- - **Personal vs Team**: **ALWAYS use the personal journal** (e.g., \`create_entry\`) by default. ONLY save to the team journal (e.g., \`team_create_entry\`) if the user explicitly requests it.
4235
- - **Create entries for**: implementations, decisions, bug fixes, milestones, user requests to "remember"
4236
- - **Search before**: major decisions, referencing prior work, understanding project context. Use \`sort_by: "importance"\` on \`search_entries\`, \`get_recent_entries\`, or \`search_by_date_range\` to surface structurally significant entries (decisions, milestones, highly-connected nodes) over simply recent ones.
4237
- - **Analyze insights**: Use cross-project insights (\`get_cross_project_insights\`) before defining architectures. Use \`team_get_collaboration_matrix\` to evaluate team health, cross-author activity patterns, and collaboration impact. Use repo insights (\`memory://github/insights\`) to gauge traction. View \`memory://insights/digest\` and \`memory://insights/team-collaboration\` for automated analytics snapshots.
4238
- - **Link entries**: implementation\u2192spec, bugfix\u2192issue, followup\u2192prior work
4239
-
4240
- ### Rule & Skill Suggestions
4241
-
4242
- When you notice the user consistently applies patterns, preferences, or workflows that could be codified:
4243
-
4244
- **Suggest adding a rule** when you observe:
4245
-
4246
- - Naming conventions, formatting preferences, or coding standards
4247
- - Testing patterns or verification steps the user always follows
4248
- - Project-specific commands, workflows, or deployment steps
4249
- - Error handling patterns or logging conventions
4250
-
4251
- **Suggest adding a skill** when you build:
4252
-
4253
- - Reusable multi-step processes (e.g., deployment, release, audit workflows)
4254
- - Project-specific templates or scaffolds
4255
- - Complex integrations or tool chains the user may repeat
4256
-
4257
- **Suggest refining existing rules/skills** when you notice:
4258
-
4259
- - A rule conflict or ambiguity causing inconsistent behavior
4260
- - An outdated pattern that no longer matches the codebase
4261
- - Missing edge cases or exceptions to an existing rule
4262
- - A skill that could be extended with new steps
4263
-
4264
- **How to act:**
4265
-
4266
- - The briefing shows **Rules** and **Skills** paths \u2014 use these to locate the files
4267
- - **Always ask the user first** \u2014 never create or modify rules/skills silently
4268
- - Frame suggestions as: "I noticed you always [pattern]. Would you like me to add/update a rule for this?"
4269
- - For skills, explain the workflow it would automate and what triggers it
4270
-
4271
- ### Native Agent Skills (NPM Distribution)
4272
-
4273
- This server leverages the \`neverinfamous-agent-skills\` package. If the user's \`SKILLS_DIR_PATH\` environment variable targets these, you have native access to foundational frameworks (\`typescript\`, \`react-best-practices\`, \`playwright-standard\`, \`golang\`, \`rust\`, \`python\`, \`docker\`, \`tailwind-css\`, \`shadcn-ui\`) and the \`github-commander\` DevOps workflows (\`issue-triage\`, \`pr-review\`, \`github-actions\`, \`copilot-audit\`, etc.). The \`adversarial-planner\` skill provides multi-pass plan review with structured critique stages.
4274
-
4275
- - The user can distribute or update these skills across their repositories by running \`npx neverinfamous-agent-skills@latest\`.
4276
- - If you need to create a new skill, reference the bundled \`skill-builder\` instructions!
4277
-
4278
- ### Hush Protocol (Team Flags)
4279
-
4280
- Flags are machine-actionable signals stored in the team database. They replace Slack/Teams noise with structured, searchable entries that surface automatically in the briefing.
4281
-
4282
- **When to create a flag** (\`pass_team_flag\`):
4283
-
4284
- - \`blocker\` \u2014 work is blocked and requires another person's action
4285
- - \`needs_review\` \u2014 code, document, or decision needs peer review
4286
- - \`help_requested\` \u2014 stuck and need guidance or pairing
4287
- - \`fyi\` \u2014 non-blocking awareness signal (completed migration, config change, etc.)
4288
-
4289
- **When to resolve** (\`resolve_team_flag\`): After the blocking condition is cleared. Include a brief resolution comment describing what was done. Resolving is idempotent \u2014 safe to call on already-resolved flags.
4290
-
4291
- **Briefing integration**: The \`memory://briefing\` payload includes \`activeFlags\` when unresolved flags exist. The user's agent rules may instruct you to render these prominently. Always check for and acknowledge active flags at session start.
4292
-
4293
- **Dashboard**: Read \`memory://flags\` to see all active (unresolved) flags. Read \`memory://flags/vocabulary\` to see the configured flag types.
4294
-
4295
- **Code Mode**: \`mj.team.passTeamFlag({ flag_type, message })\` and \`mj.team.resolveTeamFlag({ flag_id })\`.
4296
- `;
4297
- var COPILOT_REVIEW_INSTRUCTIONS = `
4298
- ## Copilot Review Patterns
4299
-
4300
- When the user has GitHub Copilot code review enabled:
4301
-
4302
- **Learn from reviews** \u2014 After a PR is merged or reviewed, use \`get_copilot_reviews(pr_number)\` to read Copilot's findings. If patterns emerge (e.g., repeated null check warnings, missing error handling), suggest adding a rule or updating existing rules. Create journal entries tagged \`copilot-finding\` and link to the PR via \`pr_number\`.
4303
-
4304
- **Pre-emptive checking** \u2014 Before creating or modifying code, search journal entries with tag \`copilot-finding\` for patterns relevant to the current work. Apply those patterns proactively to reduce review cycles.
4305
-
4306
- **How to act:**
4307
-
4308
- - The briefing shows **Rules** and **Skills** paths \u2014 use these to locate the files
4309
- - **Always ask the user first** \u2014 never create or modify rules/skills silently
4310
- - Frame suggestions as: "I noticed you always [pattern]. Would you like me to add/update a rule for this?"
4311
- - For skills, explain the workflow it would automate and what triggers it
4312
- `;
4313
- function buildQuickAccess(groups) {
4314
- let table = `
4315
- ## Quick Access
4316
-
4317
- | Purpose | Action |
4318
- | --------------- | --------------------------- |
4319
- | Session context | \`memory://briefing\` |
4320
- | Recent entries | \`memory://recent\` |
4321
- | Health/time | \`memory://health\` |
4322
- `;
4323
- if (groups.has("search")) {
4324
- table += `| Semantic search | \`semantic_search(query)\` |
4325
- `;
4326
- }
4327
- table += `| Full context | \`get-context-bundle\` prompt |
4328
- `;
4329
- return table;
4330
- }
4331
- var CODE_MODE_NAMESPACE_ROWS = [
4332
- {
4333
- group: "core",
4334
- label: "Core",
4335
- namespace: "`mj.core.*`",
4336
- example: '`mj.core.createEntry("Implemented feature X")`'
4337
- },
4338
- {
4339
- group: "search",
4340
- label: "Search",
4341
- namespace: "`mj.search.*`",
4342
- example: '`mj.search.searchEntries("performance")`'
4343
- },
4344
- {
4345
- group: "analytics",
4346
- label: "Analytics",
4347
- namespace: "`mj.analytics.*`",
4348
- example: "`mj.analytics.getStatistics()`"
4349
- },
4350
- {
4351
- group: "relationships",
4352
- label: "Relationships",
4353
- namespace: "`mj.relationships.*`",
4354
- example: '`mj.relationships.linkEntries(1, 2, "implements")`'
4355
- },
4356
- {
4357
- group: "io",
4358
- label: "IO",
4359
- namespace: "`mj.io.*`",
4360
- example: '`mj.io.importMarkdown("content")`'
4361
- },
4362
- {
4363
- group: "io",
4364
- label: "Export",
4365
- namespace: "`mj.export.*`",
4366
- example: '`mj.export.exportEntries("json")`'
4367
- },
4368
- {
4369
- group: "admin",
4370
- label: "Admin",
4371
- namespace: "`mj.admin.*`",
4372
- example: "`mj.admin.rebuildVectorIndex()`"
4373
- },
4374
- {
4375
- group: "github",
4376
- label: "GitHub",
4377
- namespace: "`mj.github.*`",
4378
- example: '`mj.github.getGithubIssues({ state: "open" })`'
4379
- },
4380
- {
4381
- group: "backup",
4382
- label: "Backup",
4383
- namespace: "`mj.backup.*`",
4384
- example: "`mj.backup.backupJournal()`"
4385
- },
4386
- {
4387
- group: "team",
4388
- label: "Team",
4389
- namespace: "`mj.team.*`",
4390
- example: '`mj.team.teamCreateEntry("Team update")`'
4391
- }
4392
- ];
4393
- function buildCodeModeInstructions(groups) {
4394
- const rows = CODE_MODE_NAMESPACE_ROWS.filter((r) => groups.has(r.group)).map(
4395
- (r) => `| ${r.label.padEnd(13)} | ${r.namespace.padEnd(20)} | ${r.example.padEnd(50)} |`
4396
- ).join("\n");
4397
- const fullSection = CODE_MODE_FULL_TEXT;
4398
- const tableStart = fullSection.indexOf("| Group");
4399
- const tableEnd = fullSection.indexOf("\n\n**Features**");
4400
- if (tableStart === -1 || tableEnd === -1) {
4401
- return "\n" + fullSection;
4402
- }
4403
- const beforeTable = fullSection.slice(0, tableStart);
4404
- const headerLine = "| Group | Namespace | Example |";
4405
- const separatorLine = "| ------------- | -------------------- | -------------------------------------------------- |";
4406
- const afterTable = fullSection.slice(tableEnd);
4407
- return "\n" + beforeTable + headerLine + "\n" + separatorLine + "\n" + rows + afterTable;
4408
- }
4409
- var CODE_MODE_FULL_TEXT = `## Code Mode (Token-Efficient Multi-Step Operations)
4410
-
4411
- For multi-step workflows (3+ operations), prefer \`mj_execute_code\` over individual tool calls.
4412
- This executes JavaScript in a sandboxed environment with all tools available as \`mj.*\` API:
4413
-
4414
- | Group | Namespace | Example |
4415
- | ------------- | -------------------- | -------------------------------------------------- |
4416
- | Core | \`mj.core.*\` | \`mj.core.createEntry("Implemented feature X")\` |
4417
- | Search | \`mj.search.*\` | \`mj.search.searchEntries("performance")\` |
4418
- | Analytics | \`mj.analytics.*\` | \`mj.analytics.getStatistics()\` |
4419
- | Relationships | \`mj.relationships.*\` | \`mj.relationships.linkEntries(1, 2, "implements")\` |
4420
- | IO | \`mj.io.*\` | \`mj.io.importMarkdown("content")\` |
4421
- | Export | \`mj.export.*\` | \`mj.export.exportEntries("json")\` |
4422
- | Admin | \`mj.admin.*\` | \`mj.admin.rebuildVectorIndex()\` |
4423
- | GitHub | \`mj.github.*\` | \`mj.github.getGithubIssues({ state: "open" })\` |
4424
- | Backup | \`mj.backup.*\` | \`mj.backup.backupJournal()\` |
4425
- | Team | \`mj.team.*\` | \`mj.team.teamCreateEntry("Team update")\` |
4426
-
4427
- **Features**: Positional args (\`createEntry("note")\`), aliases (\`mj.core.create\`), \`mj.help()\` for discovery.
4428
- **Readonly mode**: \`readonly: true\` restricts to read-only tools only. Calling a mutation method (e.g., \`mj.core.create(...)\`) in readonly mode throws an error that halts execution \u2014 the sandbox returns \`{ success: false, error: "Operation '...' is not found in group" }\`. If a group has no methods at all (fully stripped), the error says \`"no methods (read-only mode?)"\`.
4429
- **Returns**: Last expression value. Errors return \`{ success: false, error: "..." }\`.
4430
-
4431
- **GitHub Context Injection**: You can pass \`repo: 'my-repo'\` directly to \`mj_execute_code\` (e.g., \`mj_execute_code({ code, repo: 'memory-journal-mcp' })\`) to instantly bind that repository and its default Kanban board to all GitHub and Kanban tools running inside the sandbox, avoiding the need to pass \`owner\`/\`repo\` manually to individual methods inside.
4432
-
4433
- **Important \u2014 all \`mj.*\` methods return Promises. Always \`await\` them:**
4434
-
4435
- \`\`\`js
4436
- // \u2705 Correct
4437
- const result = await mj.core.recent({ limit: 5 })
4438
- return result.entries.map((e) => e.id)
4439
-
4440
- // \u274C Wrong \u2014 returns a Promise object, not the entries
4441
- const result = mj.core.recent({ limit: 5 })
4442
-
4443
- // \u2705 Discovery
4444
- const help = await mj.help() // { groups, totalMethods, usage }
4445
- const groupHelp = await mj.core.help() // { group, methods }
4446
- \`\`\`
4447
-
4448
- **\`mj.core.recent()\` return shape**: Returns \`{ entries: JournalEntry[], count: number }\` \u2014 not a plain array. Access \`.entries\` to iterate:
4449
-
4450
- \`\`\`js
4451
- const { entries, count } = await mj.core.recent({ limit: 10 })
4452
- return entries.map((e) => ({ id: e.id, content: e.content.slice(0, 50) }))
4453
- \`\`\`
4454
- `;
4455
- var GITHUB_INSTRUCTIONS = `
4456
- ## GitHub Integration
4457
-
4458
- - Include \`issue_number\`/\`pr_number\` in \`create_entry\` to auto-link
4459
- - After closing issue/merging PR \u2192 create summary entry with learnings
4460
- - CI failures \u2192 \`actions-failure-digest\` prompt or \`memory://actions/recent\`
4461
- - Kanban: \`get_kanban_board\` \u2192 \`add_kanban_item\` / \`move_kanban_item\` / \`delete_kanban_item\` \u2192 document completion (project_number auto-resolves if repo is registered)
4462
- - Milestones: \`get_github_milestones\` \u2192 track project progress, \`memory://github/milestones\`
4463
- - **Multi-Project Routing**: If \`memory://briefing\` shows "Registered Workspaces":
4464
- - **Tools**: Pass a \`repo\` parameter to ALL GitHub tools (including \`get_github_context\`) to explicitly target a specific project.
4465
- - **Resources**: You MUST use the dynamic \`{repo}\` variants for resources (e.g., \`memory://github/status/{repo}\`, \`memory://github/insights/{repo}\`) rather than the base URI (\`memory://github/status\`), which will fail with a detection error.
4466
- - **Dynamic Briefings**: You can explicitly request the briefing for a specific project by reading \`memory://briefing/{repo}\` instead of the global \`memory://briefing\` resource.
4467
- `;
4468
- var HELP_POINTERS = `
4469
- ## Help Resources
4470
-
4471
- Read \`memory://help\` for tool group index and available help resources.
4472
- Read \`memory://help/{group}\` for per-group tool reference (parameters, annotations, examples).
4473
- Read \`memory://help/gotchas\` for critical field notes and usage patterns.
4474
- Only help resources for your enabled tool groups are registered.
4475
- `;
4476
- var SERVER_ACCESS_INSTRUCTIONS = `
4477
- ## How to Access This Server
4478
-
4479
- ### Server Name Discovery
4480
-
4481
- The server name used for resource and tool calls depends on your MCP client:
4482
-
4483
- - **AntiGravity**: Prefixes tools with \`mcp_\` and uses underscores. If the server is named \`memory-journal-mcp\` in config, tools appear as \`mcp_memory-journal-mcp_create_entry\`. Use \`memory-journal-mcp\` as the server name for resource calls.
4484
- - **Cursor**: Prepends \`user-\` to the configured name. If the server is named \`memory-journal-mcp\` in config, use \`user-memory-journal-mcp\` for \`ListMcpResources\` and \`FetchMcpResource\` calls.
4485
- - **Other clients** (Claude Desktop, etc.): Likely use the configured name exactly. Only Cursor and AntiGravity have been verified \u2014 use the tool-prefix discovery method if unsure.
4486
-
4487
- To identify your server name: look at the tool name prefix. Strip the tool name suffix to get the server name. Examples: \`mcp_memory-journal-mcp_create_entry\` \u2192 \`memory-journal-mcp\`; \`user-memory-journal-mcp-create_entry\` \u2192 \`user-memory-journal-mcp\`.
4488
-
4489
- ### Calling Tools
4490
-
4491
- Use the tool functions directly \u2014 they are already available in your context by their full prefixed name.
4492
-
4493
- ### Reading Resources
4494
-
4495
- Use the resource-reading mechanism provided by your MCP client with the discovered server name and \`memory://\` URIs.
4496
-
4497
- Do NOT try to browse filesystem paths for MCP tool/resource definitions \u2014 use the MCP protocol directly.
4498
-
4499
- ## Quick Health Check
4500
-
4501
- Fetch \`memory://health\` to verify server status, database stats, and tool availability.
4502
- `;
4503
- var GOTCHAS_CONTENT = `# memory-journal-mcp \u2014 Field Notes & Gotchas
4504
-
4505
- ## \u26A0\uFE0F Critical Patterns
4506
-
4507
- - **\`autoContext\`**: Deprecated in v7.5.1. The feature was originally planned for background filesystem monitoring but has been abandoned to reduce telemetry overhead. Existing data with \`autoContext: null\` is safely ignored.
4508
- - **\`memory://tags\` vs \`list_tags\`**: Resource includes \`id\`, \`name\`, \`count\`; tool returns only \`name\`, \`count\`. Neither returns orphan tags with zero usage.
4509
- - **Tag naming**: Use lowercase with dashes (e.g., \`bug-fix\`, \`phase-2\`). Use \`merge_tags\` to consolidate duplicates (e.g., merge \`phase2\` into \`phase-2\`).
4510
- - **\`merge_tags\` behavior**: Only updates non-deleted entries. Deleted entries retain their original tags.
4511
- - **\`prStatus\` in entries**: Reflects PR state at entry creation time, not current state. Use \`get_github_pr\` for live status.
4512
- - **\`restore_backup\` behavior**: Restores entire database state. Any recent changes (new entries, tag merges via \`merge_tags\`, relationships) are reverted. A pre-restore backup is automatically created for safety.
4513
-
4514
- ## Semantic Search
4515
-
4516
- - **Indexing**: Entries are auto-indexed on creation (fire-and-forget). If index count drifts from DB count, use \`rebuild_vector_index\` or enable \`AUTO_REBUILD_INDEX=true\` for automatic reconciliation on server startup.
4517
- - **Related by ID**: Provide \`entry_id\` instead of a query string to find entries semantically related to an existing entry (reuses the existing embedding to avoid inference costs).
4518
- - **Metadata Filters**: Semantic search supports explicit filtering by \`tags\`, \`entry_type\`, \`start_date\`, and \`end_date\`.
4519
- - **Thresholds**: Default similarity threshold is 0.25. For broader matches, try 0.15-0.2. Higher values (0.4+) return only very close semantic matches. A quality floor of 0.5 is always enforced: if all results score below 0.5, a hint is included indicating results may be noise. The \`hint_on_empty\` flag (default true) only controls advisory hints for empty indexes and zero-match queries \u2014 the quality gate hint is always shown.
4520
-
4521
- ## Search
4522
-
4523
- - **Hybrid Ranking**: \`search_entries\` defaults to \`mode: 'auto'\`. Conversational prompts automatically utilize Reciprocal Rank Fusion (true Hybrid) bridging keyword and vector algorithms.
4524
- - **\`search_entries\` FTS5 query syntax**: Uses FTS5 full-text search with Porter stemmer. Phrase queries: \`"error handling"\`. Prefix: \`auth*\`. Boolean: \`deploy OR release\`, \`error NOT warning\`. Word-boundary matching ("log" matches "log" but not "catalog"). Results ranked by BM25 relevance. Falls back to LIKE substring matching for queries with unbalanced quotes or special characters.
4525
-
4526
- ## Relationships & Analytics
4527
-
4528
- - **Causal relationship types**: Use \`blocked_by\` (A was blocked by B), \`resolved\` (A resolved B), \`caused\` (A caused B) for decision tracing and failure analysis. Visualizations use distinct arrow styles for causal types.
4529
- - **Enhanced analytics**: \`get_statistics\` returns \`decisionDensity\` (significant entries per period), \`relationshipComplexity\` (avg relationships per entry), \`activityTrend\` (period-over-period growth %), and \`causalMetrics\` (counts for blocked_by/resolved/caused).
4530
- - **Importance scores**: \`get_entry_by_id\` returns \`importance\` (0.0-1.0) and \`importanceBreakdown\` showing weighted components: significance (30%), relationships (35%), causal (20%), recency (15%). \`memory://significant\` sorts entries by importance.
4531
- - **\`inactiveThresholdDays\`**: \`get_cross_project_insights\` includes \`inactiveThresholdDays: 7\` in output, documenting the inactive project classification cutoff.
4532
-
4533
- ## GitHub Metadata
4534
-
4535
- - **GitHub metadata in entries**: Entry output includes 10 GitHub fields (\`issueNumber\`, \`issueUrl\`, \`prNumber\`, \`prUrl\`, \`prStatus\`, \`projectNumber\`, \`projectOwner\`, \`workflowRunId\`, \`workflowName\`, \`workflowStatus\`) in all tool responses.
4536
-
4537
- ## Entry Operations
4538
-
4539
- - **\`delete_entry\` on soft-deleted**: \`delete_entry(id, permanent: true)\` works on previously soft-deleted entries. Returns \`success: false\` for nonexistent entries.
4540
-
4541
- ## Team Database
4542
-
4543
- - **Team cross-database search**: \`search_entries\` and \`search_by_date_range\` automatically merge team DB results when \`TEAM_DB_PATH\` is configured. Results include a \`source\` field ("personal" or "team").
4544
- - **Team vector search**: Team has its own isolated vector index. Use \`team_rebuild_vector_index\` if the team index drifts. \`team_semantic_search\` works identically to personal \`semantic_search\`.
4545
- - **Team tools without \`TEAM_DB_PATH\`**: All 25 team tools return \`{ success: false, error: "Team collaboration is not configured..." }\` \u2014 no crash, no partial results.
4546
- `;
4547
- function generateInstructions(enabledTools, prompts, latestEntry, level = "standard", enabledGroups) {
4548
- const groups = enabledGroups ?? getEnabledGroups(enabledTools);
4549
- let instructions = CORE_INSTRUCTIONS;
4550
- if (groups.has("github")) {
4551
- instructions += COPILOT_REVIEW_INSTRUCTIONS;
4552
- }
4553
- instructions += buildQuickAccess(groups);
4554
- if (groups.has("codemode")) {
4555
- instructions += buildCodeModeInstructions(groups);
4556
- }
4557
- if (level === "standard" || level === "full") {
4558
- if (groups.has("github")) {
4559
- instructions += GITHUB_INSTRUCTIONS;
4560
- }
4561
- instructions += HELP_POINTERS;
4562
- }
4563
- if (level === "full") {
4564
- instructions += SERVER_ACCESS_INSTRUCTIONS;
4565
- const activeGroups = getActiveToolGroups(enabledTools);
4566
- if (activeGroups.length > 0) {
4567
- instructions += `
4568
- ## Active Tools (${String(enabledTools.size)})
4569
- `;
4570
- for (const { group, tools } of activeGroups) {
4571
- instructions += `**${group}**: ${tools.map((t) => `\`${t}\``).join(", ")}
4572
- `;
4573
- }
4574
- }
4575
- if (prompts.length > 0) {
4576
- instructions += `
4577
- ## Prompts (${String(prompts.length)})
4578
- `;
4579
- instructions += "Pre-built templates and guided workflows:\n";
4580
- for (const prompt of prompts) {
4581
- instructions += `- \`${prompt.name}\` - ${prompt.description ?? ""}
4582
- `;
4583
- }
4584
- }
4585
- }
4586
- return instructions;
4587
- }
4588
- function getActiveToolGroups(enabledTools) {
4589
- const activeGroups = [];
4590
- for (const [group, allTools] of Object.entries(TOOL_GROUPS)) {
4591
- const enabledInGroup = allTools.filter((tool) => enabledTools.has(tool));
4592
- if (enabledInGroup.length > 0) {
4593
- activeGroups.push({ group, tools: enabledInGroup });
4594
- }
4595
- }
4596
- return activeGroups;
4597
- }
4598
- CORE_INSTRUCTIONS + COPILOT_REVIEW_INSTRUCTIONS + buildQuickAccess(new Set(Object.keys(TOOL_GROUPS))) + buildCodeModeInstructions(new Set(Object.keys(TOOL_GROUPS))) + GITHUB_INSTRUCTIONS;
4599
-
4600
- // src/handlers/prompts/workflow.ts
4601
- var MS_PER_DAY2 = 864e5;
4602
- function getWorkflowPromptDefinitions() {
4603
- return [
4604
- {
4605
- name: "find-related",
4606
- description: "Discover connected entries via semantic similarity",
4607
- icons: [ICON_PROMPT],
4608
- arguments: [
4609
- {
4610
- name: "query",
4611
- description: "Search query for finding related entries",
4612
- required: true
4613
- }
4614
- ],
4615
- handler: (args, db) => {
4616
- const query = args["query"] ?? "";
4617
- const entries = db.searchEntries(query, { limit: 5 });
4618
- return {
4619
- messages: [
4620
- {
4621
- role: "user",
4622
- content: {
4623
- type: "text",
4624
- text: `Find entries related to: "${query}"
4625
-
4626
- Recent matching entries:
4627
- ${markUntrustedContent(entries.map((e) => `- [${String(e.id)}] ${e.content.slice(0, 100)}...`).join("\n"))}`
4628
- }
4629
- }
4630
- ]
4631
- };
4632
- }
4633
- },
4634
- {
4635
- name: "prepare-standup",
4636
- description: "Daily standup summaries",
4637
- icons: [ICON_PROMPT],
4638
- arguments: [],
4639
- handler: (_args, db) => {
4640
- const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "";
4641
- const yesterday = new Date(Date.now() - MS_PER_DAY2).toISOString().split("T")[0] ?? "";
4642
- const entries = db.searchByDateRange(yesterday, today);
4643
- const digestSignal = buildDigestSignalForPrompt(db);
4644
- return {
4645
- messages: [
4646
- {
4647
- role: "user",
4648
- content: {
4649
- type: "text",
4650
- text: `${digestSignal}Prepare a standup summary based on these recent entries.
4651
- Format as:
4652
- - Yesterday: <summary>
4653
- - Today: <planned work>
4654
- - Blockers: <any blockers>
4655
-
4656
- Sources:
4657
- ${markUntrustedContent(entries.map((e) => `[${e.timestamp}] ${e.entryType}: ${e.content}`).join("\n\n"))}`
4658
- }
4659
- }
4660
- ]
4661
- };
4662
- }
4663
- },
4664
- {
4665
- name: "prepare-retro",
4666
- description: "Sprint retrospectives",
4667
- icons: [ICON_PROMPT],
4668
- arguments: [
4669
- {
4670
- name: "days",
4671
- description: "Number of days to include (default: 14)",
4672
- required: false
4673
- }
4674
- ],
4675
- handler: (args, db) => {
4676
- const days = parseInt(args["days"] ?? "14", 10);
4677
- const endDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "";
4678
- const startDate = new Date(Date.now() - days * MS_PER_DAY2).toISOString().split("T")[0] ?? "";
4679
- const entries = db.searchByDateRange(startDate, endDate);
4680
- const digestSignal = buildDigestSignalForPrompt(db);
4681
- return {
4682
- messages: [
4683
- {
4684
- role: "user",
4685
- content: {
4686
- type: "text",
4687
- text: `${digestSignal}Prepare a retrospective for the last ${String(days)} days based on these entries.
4688
- Format as:
4689
- - What went well
4690
- - What could improve
4691
- - Action items
4692
-
4693
- Sources:
4694
- ${markUntrustedContent(
4695
- entries.slice(0, 20).map((e) => `[${e.timestamp}] ${e.entryType}: ${e.content.slice(0, 200)}`).join("\n\n")
4696
- )}`
4697
- }
4698
- }
4699
- ]
4700
- };
4701
- }
4702
- },
4703
- {
4704
- name: "weekly-digest",
4705
- description: "Day-by-day weekly summaries",
4706
- icons: [ICON_PROMPT],
4707
- arguments: [],
4708
- handler: (_args, db) => {
4709
- const endDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0] ?? "";
4710
- const startDate = new Date(Date.now() - 7 * MS_PER_DAY2).toISOString().split("T")[0] ?? "";
4711
- const entries = db.searchByDateRange(startDate, endDate);
4712
- return {
4713
- messages: [
4714
- {
4715
- role: "user",
4716
- content: {
4717
- type: "text",
4718
- text: `Create a weekly digest from these entries.
4719
- Format as day-by-day summary with highlights.
4720
-
4721
- Sources:
4722
- ${markUntrustedContent(entries.map((e) => `[${e.timestamp}] ${e.entryType}: ${e.content.slice(0, 150)}`).join("\n\n"))}`
4723
- }
4724
- }
4725
- ]
4726
- };
4727
- }
4728
- },
4729
- {
4730
- name: "analyze-period",
4731
- description: "Deep period analysis with insights",
4732
- icons: [ICON_PROMPT],
4733
- arguments: [
4734
- { name: "start_date", description: "Start date (YYYY-MM-DD)", required: true },
4735
- { name: "end_date", description: "End date (YYYY-MM-DD)", required: true }
4736
- ],
4737
- handler: (args, db) => {
4738
- const startDate = args["start_date"] ?? "";
4739
- const endDate = args["end_date"] ?? "";
4740
- const entries = db.searchByDateRange(startDate, endDate);
4741
- const stats = db.getStatistics("day");
4742
- return {
4743
- messages: [
4744
- {
4745
- role: "user",
4746
- content: {
4747
- type: "text",
4748
- text: `Analyze the period ${startDate} to ${endDate}:
4749
-
4750
- Statistics: ${JSON.stringify(stats, null, 2)}
4751
-
4752
- Provide insights on patterns, productivity, and recommendations.
4753
-
4754
- Sources (${String(entries.length)} total):
4755
- ${markUntrustedContent(
4756
- entries.slice(0, 15).map((e) => `[${e.timestamp}] ${e.entryType}: ${e.content.slice(0, 100)}`).join("\n")
4757
- )}`
4758
- }
4759
- }
4760
- ]
4761
- };
4762
- }
4763
- },
4764
- {
4765
- name: "goal-tracker",
4766
- description: "Milestone and achievement tracking",
4767
- icons: [ICON_PROMPT],
4768
- arguments: [],
4769
- handler: (_args, db) => {
4770
- const entries = db.getSignificantEntries(20);
4771
- const mappedEntries = entries.map((e) => ({
4772
- id: e.id,
4773
- type: e.entryType,
4774
- timestamp: e.timestamp,
4775
- content: markUntrustedContentInline(
4776
- e.content.length > 250 ? e.content.slice(0, 250) + "..." : e.content
4777
- )
4778
- }));
4779
- return {
4780
- messages: [
4781
- {
4782
- role: "user",
4783
- content: {
4784
- type: "text",
4785
- text: `Track goals and milestones based on significant entries.
4786
- Summarize progress toward goals and highlight achievements.
4787
-
4788
- Sources:
4789
- ${JSON.stringify(mappedEntries, null, 2)}`
4790
- }
4791
- }
4792
- ]
4793
- };
4794
- }
4795
- },
4796
- {
4797
- name: "get-context-bundle",
4798
- description: "Project context with recent entries, statistics, and GitHub status hints",
4799
- icons: [ICON_PROMPT],
4800
- arguments: [],
4801
- handler: (_args, db) => {
4802
- const recent = db.getRecentEntries(5);
4803
- const stats = db.getStatistics("week");
4804
- const entrySummaries = recent.map((e) => ({
4805
- id: e.id,
4806
- type: e.entryType,
4807
- timestamp: e.timestamp,
4808
- preview: e.content.slice(0, 60) + (e.content.length > 60 ? "..." : "")
4809
- }));
4810
- return {
4811
- messages: [
4812
- {
4813
- role: "user",
4814
- content: {
4815
- type: "text",
4816
- text: `Project context bundle:
4817
-
4818
- **Statistics:** ${JSON.stringify(stats)}
4819
-
4820
- **For full GitHub status:** Fetch \`memory://github/status\`
4821
- **For full entry details:** Use \`get_entry_by_id\` with entry ID
4822
-
4823
- **Recent Entries (${String(recent.length)}):**
4824
- ${markUntrustedContent(entrySummaries.map((e) => `- #${String(e.id)} (${e.type}) ${e.preview}`).join("\n"))}`
4825
- }
4826
- }
4827
- ]
4828
- };
4829
- }
4830
- },
4831
- {
4832
- name: "get-recent-entries",
4833
- description: "Formatted recent entries",
4834
- icons: [ICON_PROMPT],
4835
- arguments: [
4836
- { name: "limit", description: "Number of entries (default: 10)", required: false }
4837
- ],
4838
- handler: (args, db) => {
4839
- const limit = parseInt(args["limit"] ?? "10", 10);
4840
- const entries = db.getRecentEntries(limit);
4841
- return {
4842
- messages: [
4843
- {
4844
- role: "user",
4845
- content: {
4846
- type: "text",
4847
- text: `Recent ${String(limit)} entries:
4848
-
4849
- ${markUntrustedContent(entries.map((e) => `## ${e.timestamp} (${e.entryType})
4850
-
4851
- ${e.content}
4852
-
4853
- Tags: ${e.tags.join(", ") || "none"}`).join("\n\n---\n\n"))}`
4854
- }
4855
- }
4856
- ]
4857
- };
4858
- }
4859
- },
4860
- {
4861
- name: "confirm-briefing",
4862
- description: "Acknowledge session context received from memory://briefing to inform the user",
4863
- icons: [ICON_PROMPT],
4864
- arguments: [],
4865
- handler: (_args, db) => {
4866
- const recent = db.getRecentEntries(3);
4867
- const stats = db.getStatistics("week");
4868
- const totalEntries = stats.totalEntries ?? 0;
4869
- const entrySummary = recent.length > 0 ? recent.map(
4870
- (e) => ` - #${String(e.id)} (${e.entryType}) ${e.content.slice(0, 40)}...`
4871
- ).join("\n") : " - No entries yet";
4872
- return {
4873
- messages: [
4874
- {
4875
- role: "user",
4876
- content: {
4877
- type: "text",
4878
- text: `Generate a briefing acknowledgment for the user with this context:
4879
-
4880
- **Session Context Received:**
4881
- - **Journal**: ${String(totalEntries)} total entries
4882
- - **Latest Entries**:
4883
- ${markUntrustedContent(entrySummary)}
4884
-
4885
- **My Behaviors:**
4886
- - Create entries for: implementations, decisions, bug fixes, milestones
4887
- - Search before: major decisions, referencing prior work
4888
- - Link entries: implementation\u2192spec, bugfix\u2192issue
4889
-
4890
- **For More Context:**
4891
- - Full entries: \`memory://recent\` or \`get_entry_by_id(ID)\`
4892
- - GitHub status: \`memory://github/status\`
4893
- - Repo insights: \`memory://github/insights\` (stars, traffic, clones)
4894
- - Full health: \`memory://health\`
4895
-
4896
- Please confirm this context to the user in a concise, friendly format. Use a table if helpful.`
4897
- }
4898
- }
4899
- ]
4900
- };
4901
- }
4902
- },
4903
- {
4904
- name: "session-summary",
4905
- description: "Create a session summary entry capturing what was accomplished, pending items, and context for the next session",
4906
- icons: [ICON_PROMPT],
4907
- arguments: [],
4908
- handler: (_args, db) => {
4909
- const recent = db.getRecentEntries(5);
4910
- const entrySummary = recent.length > 0 ? recent.map(
4911
- (e) => `- #${String(e.id)} (${e.entryType}) ${e.content.slice(0, 80)}${e.content.length > 80 ? "..." : ""}`
4912
- ).join("\n") : "- No entries yet";
4913
- return {
4914
- messages: [
4915
- {
4916
- role: "user",
4917
- content: {
4918
- type: "text",
4919
- text: `Create a session summary journal entry based on this context:
4920
-
4921
- **Instructions:**
4922
- 1. Summarize what was accomplished in this session (key changes, decisions, files modified)
4923
- 2. Note what's unfinished or blocked (pending items, open questions)
4924
- 3. Include context for the next session (relevant entry IDs, branch names, PR numbers)
4925
- 4. Use \`entry_type: "retrospective"\` and tag with \`session-summary\`
4926
-
4927
- **Recent Entries:**
4928
- ${markUntrustedContent(entrySummary)}`
4929
- }
4930
- }
4931
- ]
4932
- };
4933
- }
4934
- },
4935
- {
4936
- name: "team-session-summary",
4937
- description: "Create a session summary entry for the team capturing what was accomplished, pending items, and context for the next team session",
4938
- icons: [ICON_PROMPT],
4939
- arguments: [],
4940
- handler: (_args, _db, teamDb) => {
4941
- if (!teamDb) {
4942
- throw new ConfigurationError("Team database not configured");
4943
- }
4944
- const recent = teamDb.getRecentEntries(5);
4945
- const entrySummary = recent.length > 0 ? recent.map(
4946
- (e) => `- #${String(e.id)} (${e.entryType}) ${e.content.slice(0, 80)}${e.content.length > 80 ? "..." : ""}`
4947
- ).join("\n") : "- No entries yet";
4948
- return {
4949
- messages: [
4950
- {
4951
- role: "user",
4952
- content: {
4953
- type: "text",
4954
- text: `Create a team session summary journal entry based on this context:
4955
-
4956
- **Instructions:**
4957
- 1. Summarize what the team accomplished in this session (key changes, decisions, files modified)
4958
- 2. Note what's unfinished or blocked for the team (pending items, open questions)
4959
- 3. Include context for the next team session (relevant entry IDs, branch names, PR numbers)
4960
- 4. Use \`entry_type: "retrospective"\` and tag with \`session-summary\`
4961
- 5. YOU MUST USE \`team_create_entry\` OR \`mj.team.create\` TO SAVE THIS ENTRY.
4962
-
4963
- **Recent Team Entries:**
4964
- ${markUntrustedContent(entrySummary)}`
4965
- }
4966
- }
4967
- ]
4968
- };
4969
- }
4970
- }
4971
- ];
4972
- }
4973
- function buildDigestSignalForPrompt(db) {
4974
- const snapshot = db.getLatestAnalyticsSnapshot?.("digest");
4975
- if (!snapshot) return "";
4976
- const data = snapshot.data;
4977
- const lines = ["[Analytics Context]"];
4978
- const growth = data["activityGrowthPercent"];
4979
- const currentEntries = data["currentPeriodEntries"];
4980
- if (growth !== null && growth !== void 0) {
4981
- const sign = growth >= 0 ? "+" : "";
4982
- lines.push(
4983
- `Activity: ${sign}${String(growth)}% vs. last period (${String(currentEntries)} entries)`
4984
- );
4985
- }
4986
- const sigMultiplier = data["significanceMultiplier"];
4987
- const sigCount = data["currentPeriodSignificant"];
4988
- if (sigMultiplier !== null && sigMultiplier !== void 0 && sigMultiplier > 1.5) {
4989
- lines.push(
4990
- `Significance: ${String(sigCount)} significant entries (${String(sigMultiplier)}\xD7 historical avg)`
4991
- );
4992
- }
4993
- const stale = data["staleProjects"];
4994
- if (stale && stale.length > 0) {
4995
- const staleStr = stale.map((p) => `P${String(p.projectNumber)} (${String(p.daysSilent)}d silent)`).join(", ");
4996
- lines.push(`\u26A0 Stale: ${staleStr}`);
4997
- }
4998
- if (lines.length <= 1) return "";
4999
- return lines.join("\\n") + "\\n\\n";
5000
- }
5001
-
5002
- // src/handlers/prompts/github.ts
5003
- function formatPromptEntries(entries, maxCount = 50) {
5004
- return entries.slice(0, maxCount).map(
5005
- (e) => ({
5006
- id: e.id,
5007
- type: e.entryType,
5008
- timestamp: e.timestamp,
5009
- content: markUntrustedContentInline(
5010
- e.content.length > 250 ? e.content.slice(0, 250) + "..." : e.content
5011
- )
5012
- })
5013
- );
5014
- }
5015
- function getGitHubPromptDefinitions() {
5016
- return [
5017
- {
5018
- name: "project-status-summary",
5019
- description: "GitHub Project status reports",
5020
- icons: [ICON_PROMPT],
5021
- arguments: [
5022
- { name: "project_number", description: "GitHub Project number", required: true }
5023
- ],
5024
- handler: (args, db) => {
5025
- const projectNumber = parseInt(args["project_number"] ?? "0", 10);
5026
- const entries = db.searchEntries("", { projectNumber, limit: 20 });
5027
- return {
5028
- messages: [
5029
- {
5030
- role: "user",
5031
- content: {
5032
- type: "text",
5033
- text: `Generate a status summary for Project #${String(projectNumber)}.
5034
- Provide: overview, recent activity, blockers, next steps.
5035
-
5036
- Sources:
5037
- ${JSON.stringify(formatPromptEntries(entries), null, 2)}`
5038
- }
5039
- }
5040
- ]
5041
- };
5042
- }
5043
- },
5044
- {
5045
- name: "pr-summary",
5046
- description: "Pull request journal activity summary",
5047
- icons: [ICON_PROMPT],
5048
- arguments: [{ name: "pr_number", description: "Pull request number", required: true }],
5049
- handler: (args, db) => {
5050
- const prNumber = parseInt(args["pr_number"] ?? "0", 10);
5051
- const entries = db.searchEntries("", { prNumber, limit: 100 }).reverse();
5052
- return {
5053
- messages: [
5054
- {
5055
- role: "user",
5056
- content: {
5057
- type: "text",
5058
- text: `Summarize PR #${String(prNumber)} activity.
5059
- Provide: summary of changes, decisions made, testing done.
5060
-
5061
- Sources:
5062
- ${JSON.stringify(formatPromptEntries(entries), null, 2)}`
5063
- }
5064
- }
5065
- ]
5066
- };
5067
- }
5068
- },
5069
- {
5070
- name: "code-review-prep",
5071
- description: "Comprehensive PR review preparation",
5072
- icons: [ICON_PROMPT],
5073
- arguments: [{ name: "pr_number", description: "Pull request number", required: true }],
5074
- handler: (args, db) => {
5075
- const prNumber = parseInt(args["pr_number"] ?? "0", 10);
5076
- const entries = db.searchEntries("", { prNumber, limit: 100 }).reverse();
5077
- return {
5078
- messages: [
5079
- {
5080
- role: "user",
5081
- content: {
5082
- type: "text",
5083
- text: `Prepare for code review of PR #${String(prNumber)}.
5084
- Provide: review checklist, areas of concern, testing recommendations.
5085
-
5086
- Sources:
5087
- ${JSON.stringify(formatPromptEntries(entries), null, 2)}`
5088
- }
5089
- }
5090
- ]
5091
- };
5092
- }
5093
- },
5094
- {
5095
- name: "pr-retrospective",
5096
- description: "Completed PR analysis with learnings",
5097
- icons: [ICON_PROMPT],
5098
- arguments: [{ name: "pr_number", description: "Pull request number", required: true }],
5099
- handler: (args, db) => {
5100
- const prNumber = parseInt(args["pr_number"] ?? "0", 10);
5101
- const entries = db.searchEntries("", { prNumber, limit: 100 }).reverse();
5102
- return {
5103
- messages: [
5104
- {
5105
- role: "user",
5106
- content: {
5107
- type: "text",
5108
- text: `Retrospective for PR #${String(prNumber)}.
5109
- Provide: what went well, challenges, lessons learned.
5110
-
5111
- Sources:
5112
- ${JSON.stringify(formatPromptEntries(entries), null, 2)}`
5113
- }
5114
- }
5115
- ]
5116
- };
5117
- }
5118
- },
5119
- {
5120
- name: "actions-failure-digest",
5121
- description: "CI/CD failure analysis with root cause identification",
5122
- icons: [ICON_PROMPT],
5123
- arguments: [],
5124
- handler: (_args, db) => {
5125
- const entries = db.getWorkflowActionEntries(20);
5126
- return {
5127
- messages: [
5128
- {
5129
- role: "user",
5130
- content: {
5131
- type: "text",
5132
- text: `Analyze CI/CD failures from these workflow entries.
5133
- Provide: failure patterns, root causes, remediation steps.
5134
-
5135
- Sources:
5136
- ${JSON.stringify(formatPromptEntries(entries), null, 2)}`
5137
- }
5138
- }
5139
- ]
5140
- };
5141
- }
5142
- },
5143
- {
5144
- name: "project-milestone-tracker",
5145
- description: "Milestone progress tracking",
5146
- icons: [ICON_PROMPT],
5147
- arguments: [
5148
- { name: "project_number", description: "GitHub Project number", required: true }
5149
- ],
5150
- handler: (args, db) => {
5151
- const projectNumber = parseInt(args["project_number"] ?? "0", 10);
5152
- const entries = db.getSignificantEntries(100, projectNumber);
5153
- return {
5154
- messages: [
5155
- {
5156
- role: "user",
5157
- content: {
5158
- type: "text",
5159
- text: `Track milestones for Project #${String(projectNumber)}.
5160
- Provide: progress summary, upcoming milestones, timeline.
5161
-
5162
- Sources:
5163
- ${JSON.stringify(formatPromptEntries(entries), null, 2)}`
5164
- }
5165
- }
5166
- ]
5167
- };
5168
- }
5169
- }
5170
- ];
5171
- }
5172
-
5173
- // src/handlers/prompts/index.ts
5174
- function getPrompts() {
5175
- const prompts = getAllPromptDefinitions();
5176
- return prompts.map((p) => ({
5177
- name: p.name,
5178
- description: p.description,
5179
- arguments: p.arguments,
5180
- icons: p.icons
5181
- }));
5182
- }
5183
- function getPrompt(name, args, db, teamDb) {
5184
- const prompts = getAllPromptDefinitions();
5185
- const prompt = prompts.find((p) => p.name === name);
5186
- if (!prompt) {
5187
- throw new ResourceNotFoundError("Prompt", name);
5188
- }
5189
- return prompt.handler(args, db, teamDb);
5190
- }
5191
- function getAllPromptDefinitions() {
5192
- return [...getWorkflowPromptDefinitions(), ...getGitHubPromptDefinitions()];
5193
- }
5194
-
5195
- // src/handlers/resources/core/instructions.ts
5196
- var instructionsResource = {
5197
- uri: "memory://instructions",
5198
- name: "Server Instructions",
5199
- title: "Full Server Behavioral Guidance",
5200
- description: "Full server instructions for AI agents.",
5201
- mimeType: "text/markdown",
5202
- icons: [ICON_BRIEFING],
5203
- annotations: withPriority(0.95, ASSISTANT_FOCUSED),
5204
- handler: (_uri, context) => {
5205
- const level = "full";
5206
- const allToolNames = new Set(getAllToolNames());
5207
- const enabledTools = context.filterConfig?.enabledTools ?? allToolNames;
5208
- const enabledGroups = context.filterConfig ? getEnabledGroups(context.filterConfig.enabledTools) : void 0;
5209
- const prompts = getPrompts().map((p) => {
5210
- const prompt = p;
5211
- return { name: prompt.name, description: prompt.description };
5212
- });
5213
- const instructions = generateInstructions(
5214
- enabledTools,
5215
- prompts,
5216
- void 0,
5217
- level,
5218
- enabledGroups
5219
- );
5220
- return {
5221
- data: instructions
5222
- };
5223
- }
5224
- };
5225
-
5226
- // src/handlers/resources/core/health.ts
5227
- function getTotalToolCount() {
5228
- return getAllToolNames().length;
5229
- }
5230
- var healthResource = {
5231
- uri: "memory://health",
5232
- name: "Server Health",
5233
- title: "Server Health & Diagnostics",
5234
- description: "Server health status including database, backups, vector index (real-time stats), and tool filter status",
5235
- mimeType: "application/json",
5236
- icons: [ICON_HEALTH],
5237
- annotations: {
5238
- ...HIGH_PRIORITY,
5239
- audience: ["assistant"]
5240
- },
5241
- handler: (_uri, context) => {
5242
- const dbHealth = context.db.getHealthStatus();
5243
- let vectorIndex = null;
5244
- if (context.vectorManager) {
5245
- try {
5246
- const stats = context.vectorManager.getStats();
5247
- vectorIndex = {
5248
- available: true,
5249
- itemCount: stats.itemCount,
5250
- modelName: stats.modelName,
5251
- isReady: stats.isReady
5252
- };
5253
- } catch {
5254
- vectorIndex = { available: false, itemCount: 0, modelName: null, isReady: false };
5255
- }
5256
- }
5257
- const totalTools = getTotalToolCount();
5258
- const toolFilter = {
5259
- active: context.filterConfig !== null && context.filterConfig !== void 0,
5260
- enabledCount: context.filterConfig?.enabledTools.size ?? totalTools,
5261
- totalCount: totalTools,
5262
- filterString: context.filterConfig?.raw ?? null
5263
- };
5264
- const lastModified = (/* @__PURE__ */ new Date()).toISOString();
5265
- const metricsSummary = (() => {
5266
- try {
5267
- const s = context.runtime?.metrics.getSummary();
5268
- if (!s) return null;
5269
- return {
5270
- totalCalls: s.totalCalls,
5271
- totalErrors: s.totalErrors,
5272
- totalOutputTokens: s.totalOutputTokens,
5273
- upSince: s.upSince
5274
- };
5275
- } catch {
5276
- return null;
5277
- }
5278
- })();
5279
- return {
5280
- data: {
5281
- ...dbHealth,
5282
- vectorIndex,
5283
- toolFilter,
5284
- teamDatabase: context.teamDb ? {
5285
- configured: true,
5286
- ...context.teamDb.getHealthStatus()
5287
- } : { configured: false },
5288
- scheduler: context.scheduler ? context.scheduler.getStatus() : { active: false, jobs: [] },
5289
- audit: context.runtime?.auditLogger ? {
5290
- droppedCount: context.runtime.auditLogger.droppedCount,
5291
- status: context.runtime.auditLogger.droppedCount > 0 ? "degraded" : "ok"
5292
- } : { status: "unknown" },
5293
- metrics: metricsSummary,
5294
- timestamp: lastModified
5295
- },
5296
- annotations: { lastModified }
5297
- };
5298
- }
5299
- };
5300
- var recentResource = {
5301
- uri: "memory://recent",
5302
- name: "Recent Entries",
5303
- title: "Recent Journal Entries",
5304
- description: "10 most recent journal entries",
5305
- mimeType: "application/json",
5306
- icons: [ICON_CLOCK],
5307
- annotations: withPriority(0.8, ASSISTANT_FOCUSED),
5308
- handler: (_uri, context) => {
5309
- const entries = context.db.getRecentEntries(10);
5310
- const lastModified = entries[0]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
5311
- return {
5312
- data: { entries, count: entries.length },
5313
- annotations: { lastModified }
5314
- };
5315
- }
5316
- };
5317
- var significantResource = {
5318
- uri: "memory://significant",
5319
- name: "Significant Entries",
5320
- title: "Significant Milestones",
5321
- description: "Significant milestones and breakthroughs",
5322
- mimeType: "application/json",
5323
- icons: [ICON_STAR],
5324
- annotations: withPriority(0.7, ASSISTANT_FOCUSED),
5325
- handler: (_uri, context) => {
5326
- const entries = context.db.getSignificantEntries(100);
5327
- const now = Date.now();
5328
- const MS_PER_DAY3 = 864e5;
5329
- const RECENCY_WINDOW_DAYS2 = 90;
5330
- const MAX_REL_SCORE_AT = 5;
5331
- const MAX_CAUSAL_SCORE_AT2 = 3;
5332
- const entryIds = entries.map((e) => e.id);
5333
- const relationshipsMap = context.db.getRelationshipsForEntries(entryIds);
5334
- const entriesWithImportance = entries.map((entry) => {
5335
- const relationships = relationshipsMap.get(entry.id) ?? [];
5336
- const relCount = relationships.length;
5337
- const causalCount = relationships.filter(
5338
- (r) => ["blocked_by", "resolved", "caused"].includes(r.relationshipType)
5339
- ).length;
5340
- const timestampMs = new Date(entry.timestamp).getTime();
5341
- const daysSince = Math.floor((now - timestampMs) / MS_PER_DAY3);
5342
- const recency = Math.max(0, 1 - daysSince / RECENCY_WINDOW_DAYS2);
5343
- const importance = Math.round(
5344
- (1 * 0.3 + Math.min(relCount / MAX_REL_SCORE_AT, 1) * 0.35 + Math.min(causalCount / MAX_CAUSAL_SCORE_AT2, 1) * 0.2 + recency * 0.15) * 100
5345
- ) / 100;
5346
- return { ...entry, importance, timestampMs };
5347
- });
5348
- entriesWithImportance.sort((a, b) => {
5349
- if (b.importance !== a.importance) {
5350
- return b.importance - a.importance;
5351
- }
5352
- return b.timestampMs - a.timestampMs;
5353
- });
5354
- const top20 = entriesWithImportance.slice(0, 20);
5355
- return { entries: top20, count: top20.length };
5356
- }
5357
- };
5358
- var tagsResource = {
5359
- uri: "memory://tags",
5360
- name: "All Tags",
5361
- title: "Tag List",
5362
- description: "All available tags with usage counts",
5363
- mimeType: "application/json",
5364
- icons: [ICON_TAG],
5365
- annotations: { ...LOW_PRIORITY, audience: ["assistant"] },
5366
- handler: (_uri, context) => {
5367
- const tags = context.db.listTags();
5368
- const mappedTags = tags.map((t) => ({
5369
- id: t.id,
5370
- name: t.name,
5371
- count: t.usageCount
5372
- }));
5373
- return { tags: mappedTags, count: mappedTags.length };
5374
- }
5375
- };
5376
- var statisticsResource = {
5377
- uri: "memory://statistics",
5378
- name: "Statistics",
5379
- title: "Journal Statistics",
5380
- description: "Overall journal statistics",
5381
- mimeType: "application/json",
5382
- icons: [ICON_ANALYTICS],
5383
- annotations: { ...LOW_PRIORITY, audience: ["assistant"] },
5384
- handler: (_uri, context) => {
5385
- return context.db.getStatistics("week");
5386
- }
5387
- };
5388
- var cachedRulesMap = /* @__PURE__ */ new Map();
5389
- var RULES_CACHE_TTL_MS = 5 * 60 * 1e3;
5390
- var rulesResource = {
5391
- uri: "memory://rules",
5392
- name: "Rules File",
5393
- title: "Agent Rules & Coding Standards",
5394
- description: "Contents of the configured RULES_FILE_PATH (agent rules / GEMINI.md)",
5395
- mimeType: "text/markdown",
5396
- icons: [ICON_BRIEFING],
5397
- annotations: withPriority(0.7, ASSISTANT_FOCUSED),
5398
- handler: async (_uri, context) => {
5399
- const rulesPath = context.briefingConfig?.rulesFilePath ?? process.env["RULES_FILE_PATH"];
5400
- if (!rulesPath) {
5401
- return {
5402
- data: {
5403
- configured: false,
5404
- message: "RULES_FILE_PATH is not configured. Set this env var to serve rules content."
5405
- }
5406
- };
5407
- }
5408
- try {
5409
- const allowedRoots = context.briefingConfig?.allowedIoRoots ?? [];
5410
- const expandedRoots = [...allowedRoots, path4.dirname(rulesPath)];
5411
- assertSafeFilePath(rulesPath, expandedRoots);
5412
- } catch (err) {
5413
- return {
5414
- data: {
5415
- configured: true,
5416
- error: err instanceof Error ? err.message : String(err)
5417
- }
5418
- };
5419
- }
5420
- try {
5421
- const cached = cachedRulesMap.get(rulesPath);
5422
- if (cached && Date.now() - cached.timestamp < RULES_CACHE_TTL_MS) {
5423
- const stat2 = await fs2.promises.stat(rulesPath).catch(() => ({ mtimeMs: Date.now() }));
5424
- return {
5425
- data: cached.content,
5426
- annotations: {
5427
- lastModified: new Date(stat2.mtimeMs).toISOString()
5428
- }
5429
- };
5430
- }
5431
- const file = await fs2.promises.open(rulesPath, "r");
5432
- try {
5433
- const stat2 = await file.stat();
5434
- if (stat2.size > 1024 * 1024) {
5435
- throw new Error("Rules file exceeds 1MB limit");
5436
- }
5437
- const content = await file.readFile("utf8");
5438
- cachedRulesMap.set(rulesPath, {
5439
- content,
5440
- timestamp: Date.now()
5441
- });
5442
- if (cachedRulesMap.size > 100) {
5443
- const firstKey = cachedRulesMap.keys().next().value;
5444
- if (firstKey) cachedRulesMap.delete(firstKey);
5445
- }
5446
- } finally {
5447
- await file.close();
5448
- }
5449
- const cachedData = cachedRulesMap.get(rulesPath);
5450
- return {
5451
- data: cachedData ? cachedData.content : "",
5452
- annotations: {
5453
- lastModified: new Date(
5454
- cachedData ? cachedData.timestamp : Date.now()
5455
- ).toISOString()
5456
- }
5457
- };
5458
- } catch (err) {
5459
- const message = err instanceof Error ? err.message : String(err);
5460
- return {
5461
- data: {
5462
- configured: true,
5463
- error: `Could not read rules file: ${message}`
5464
- // Removed configuration path disclosure
5465
- }
5466
- };
5467
- }
5468
- }
5469
- };
5470
- var workflowsResource = {
5471
- uri: "memory://workflows",
5472
- name: "Workflows",
5473
- title: "Agent Workflow Summaries",
5474
- description: "Summary of available agent workflows from the configured workflow directory",
5475
- mimeType: "application/json",
5476
- icons: [ICON_BRIEFING],
5477
- annotations: { ...MEDIUM_PRIORITY, audience: ["assistant"] },
5478
- handler: (_uri, context) => {
5479
- const workflowSummary = context.briefingConfig?.workflowSummary ?? process.env["MEMORY_JOURNAL_WORKFLOW_SUMMARY"];
5480
- if (workflowSummary === void 0 || workflowSummary === "") {
5481
- return {
5482
- data: {
5483
- configured: false,
5484
- message: "No workflow summary is available. Set MEMORY_JOURNAL_WORKFLOW_SUMMARY env var or use --workflow-summary."
5485
- }
5486
- };
5487
- }
5488
- return {
5489
- data: {
5490
- configured: true,
5491
- summary: workflowSummary
5492
- }
5493
- };
5494
- }
5495
- };
5496
- var cachedSkillsMap = /* @__PURE__ */ new Map();
5497
- var SKILLS_CACHE_TTL_MS = 5 * 60 * 1e3;
5498
- function getShippedSkillsDir() {
5499
- try {
5500
- const thisFile = fileURLToPath(import.meta.url);
5501
- let dir = path4.dirname(thisFile);
5502
- while (true) {
5503
- const pkgJsonPath = path4.join(dir, "package.json");
5504
- if (fs2.existsSync(pkgJsonPath)) {
5505
- const shipped = path4.join(dir, "skills");
5506
- return fs2.existsSync(shipped) ? shipped : void 0;
5507
- }
5508
- const parent = path4.dirname(dir);
5509
- if (parent === dir) {
5510
- break;
5511
- }
5512
- dir = parent;
5513
- }
5514
- return void 0;
5515
- } catch {
5516
- return void 0;
5517
- }
5518
- }
5519
- async function scanSkillsDir(dir, source) {
5520
- if (!fs2.existsSync(dir)) return [];
5521
- const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
5522
- const skills = [];
5523
- for (const entry of entries) {
5524
- if (!entry.isDirectory()) continue;
5525
- const skillMdPath = path4.join(dir, entry.name, "SKILL.md");
5526
- if (!fs2.existsSync(skillMdPath)) continue;
5527
- const content = await fs2.promises.readFile(skillMdPath, "utf8");
5528
- const lines = content.split("\n");
5529
- const excerptLine = lines.find(
5530
- (l) => l.trim().length > 0 && !l.startsWith("#") && !l.startsWith("---")
5531
- );
5532
- const excerpt = excerptLine ? markUntrustedContentInline(excerptLine.trim().slice(0, 160)) : "";
5533
- skills.push({ name: entry.name, path: skillMdPath, excerpt, source });
5534
- }
5535
- return skills;
5536
- }
5537
- var skillsResource = {
5538
- uri: "memory://skills",
5539
- name: "Skills",
5540
- title: "Agent Skills Index",
5541
- description: "Index of available agent skills (shipped + user-configured via SKILLS_DIR_PATH)",
5542
- mimeType: "application/json",
5543
- icons: [ICON_BRIEFING],
5544
- annotations: { ...MEDIUM_PRIORITY, audience: ["assistant"] },
5545
- handler: async (_uri, context) => {
5546
- const userSkillsDir = context.briefingConfig?.skillsDirPath ?? process.env["SKILLS_DIR_PATH"];
5547
- const shippedSkillsDir = getShippedSkillsDir();
5548
- const hasAnySource = !!userSkillsDir || !!shippedSkillsDir;
5549
- if (userSkillsDir) {
5550
- try {
5551
- const allowedRoots = context.briefingConfig?.allowedIoRoots ?? [];
5552
- const expandedRoots = [...allowedRoots, userSkillsDir];
5553
- assertSafeDirectoryPath(userSkillsDir, expandedRoots);
5554
- if (!fs2.existsSync(userSkillsDir)) {
5555
- throw new Error(`Configured SKILLS_DIR_PATH does not exist: ${userSkillsDir}`);
5556
- }
5557
- } catch (err) {
5558
- return {
5559
- data: {
5560
- configured: true,
5561
- error: err instanceof Error ? err.message : String(err),
5562
- skills: [],
5563
- count: 0
5564
- }
5565
- };
5566
- }
5567
- }
5568
- if (!hasAnySource) {
5569
- return {
5570
- data: {
5571
- configured: false,
5572
- message: "No skills available. Set SKILLS_DIR_PATH to index user skills."
5573
- }
5574
- };
5575
- }
5576
- try {
5577
- const currentDirs = `${userSkillsDir ?? ""}|${shippedSkillsDir ?? ""}`;
5578
- const cached = cachedSkillsMap.get(currentDirs);
5579
- if (cached && Date.now() - cached.timestamp < SKILLS_CACHE_TTL_MS) {
5580
- return {
5581
- data: {
5582
- configured: true,
5583
- // Prevent path disclosure of host directories
5584
- skills: cached.skills.map((s) => ({
5585
- name: s.name,
5586
- excerpt: s.excerpt,
5587
- source: s.source
5588
- })),
5589
- count: cached.skills.length
5590
- }
5591
- };
5592
- }
5593
- const skillMap = /* @__PURE__ */ new Map();
5594
- if (shippedSkillsDir) {
5595
- for (const skill of await scanSkillsDir(shippedSkillsDir, "shipped")) {
5596
- skillMap.set(skill.name, skill);
5597
- }
5598
- }
5599
- if (userSkillsDir) {
5600
- for (const skill of await scanSkillsDir(userSkillsDir, "user")) {
5601
- skillMap.set(skill.name, skill);
5602
- }
5603
- }
5604
- const skills = [...skillMap.values()].sort((a, b) => a.name.localeCompare(b.name));
5605
- cachedSkillsMap.set(currentDirs, {
5606
- skills,
5607
- timestamp: Date.now()
5608
- });
5609
- if (cachedSkillsMap.size > 100) {
5610
- const firstKey = cachedSkillsMap.keys().next().value;
5611
- if (firstKey) cachedSkillsMap.delete(firstKey);
5612
- }
5613
- return {
5614
- data: {
5615
- configured: true,
5616
- // Prevent path disclosure of host directories
5617
- skills: skills.map((s) => ({
5618
- name: s.name,
5619
- excerpt: s.excerpt,
5620
- source: s.source
5621
- })),
5622
- count: skills.length
5623
- }
5624
- };
5625
- } catch (err) {
5626
- const message = err instanceof Error ? err.message : String(err);
5627
- return {
5628
- data: {
5629
- configured: true,
5630
- error: `Could not scan skills directory: ${message}`,
5631
- skills: [],
5632
- count: 0
5633
- }
5634
- };
5635
- }
5636
- }
5637
- };
5638
-
5639
- // src/handlers/resources/core/metrics-resource.ts
5640
- function nowIso() {
5641
- return (/* @__PURE__ */ new Date()).toISOString();
5642
- }
5643
- var metricsSummaryResource = {
5644
- uri: "memory://metrics/summary",
5645
- name: "Metrics Summary",
5646
- title: "Tool Call Metrics Summary",
5647
- description: "Aggregate metrics across all tool calls since server start. Includes total calls, errors, duration, and token estimates.",
5648
- mimeType: "text/plain",
5649
- annotations: {
5650
- ...HIGH_PRIORITY,
5651
- audience: ["assistant"]
5652
- },
5653
- handler: (_uri, ctx) => {
5654
- const lastModified = nowIso();
5655
- const s = ctx.runtime?.metrics.getSummary();
5656
- if (!s) {
5657
- return {
5658
- data: "metrics_summary:\n error: Metrics not available\n",
5659
- annotations: { lastModified }
5660
- };
5661
- }
5662
- const errorRate = s.totalCalls > 0 ? (s.totalErrors / s.totalCalls * 100).toFixed(1) : "0.0";
5663
- const avgDuration = s.totalCalls > 0 ? Math.round(s.totalDurationMs / s.totalCalls) : 0;
5664
- const text = `metrics_summary:
5665
- up_since: ${s.upSince}
5666
- as_of: ${lastModified}
5667
- total_calls: ${s.totalCalls}
5668
- total_errors: ${s.totalErrors}
5669
- error_rate_pct: ${errorRate}
5670
- total_duration_ms: ${s.totalDurationMs}
5671
- avg_duration_ms: ${avgDuration}
5672
- total_input_tokens: ${s.totalInputTokens}
5673
- total_output_tokens: ${s.totalOutputTokens}
5674
- tools_called: ${Object.keys(s.toolBreakdown).length}
5675
- `;
5676
- return { data: text, annotations: { lastModified } };
5677
- }
5678
- };
5679
- var metricsTokensResource = {
5680
- uri: "memory://metrics/tokens",
5681
- name: "Metrics Tokens",
5682
- title: "Token Usage Breakdown by Tool",
5683
- description: "Per-tool token usage breakdown sorted by total output tokens. Use this to identify which tools are consuming the most context window.",
5684
- mimeType: "text/plain",
5685
- annotations: {
5686
- ...MEDIUM_PRIORITY,
5687
- audience: ["assistant"]
5688
- },
5689
- handler: (_uri, ctx) => {
5690
- const lastModified = nowIso();
5691
- const breakdown = ctx.runtime?.metrics.getTokenBreakdown() ?? [];
5692
- if (breakdown.length === 0) {
5693
- return {
5694
- data: `token_breakdown:
5695
- note: No tool calls recorded yet.
5696
- as_of: ${lastModified}
5697
- `,
5698
- annotations: { lastModified }
5699
- };
5700
- }
5701
- const rows = breakdown.map(
5702
- (t) => ` - tool: ${t.toolName}
5703
- calls: ${t.callCount}
5704
- input_tokens: ${t.inputTokens}
5705
- output_tokens: ${t.outputTokens}
5706
- avg_output_tokens: ${t.avgOutputTokens}`
5707
- ).join("\n");
5708
- const text = `token_breakdown:
5709
- as_of: ${lastModified}
5710
- ${rows}
5711
- `;
5712
- return { data: text, annotations: { lastModified } };
5713
- }
5714
- };
5715
- var metricsSystemResource = {
5716
- uri: "memory://metrics/system",
5717
- name: "Metrics System",
5718
- title: "System Metrics",
5719
- description: "Process-level system metrics: memory usage, uptime, Node.js version, and platform.",
5720
- mimeType: "text/plain",
5721
- annotations: {
5722
- ...MEDIUM_PRIORITY,
5723
- audience: ["assistant"]
5724
- },
5725
- handler: (_uri, ctx) => {
5726
- const lastModified = nowIso();
5727
- const sys = ctx.runtime?.metrics.getSystemMetrics();
5728
- if (!sys) {
5729
- return {
5730
- data: "system_metrics:\n error: Metrics not available\n",
5731
- annotations: { lastModified }
5732
- };
5733
- }
5734
- const text = `system_metrics:
5735
- up_since: ${sys.upSince}
5736
- uptime_seconds: ${sys.uptimeSeconds}
5737
- process_memory_mb: ${sys.processMemoryMb}
5738
- node_version: ${sys.nodeVersion}
5739
- platform: ${sys.platform}
5740
- as_of: ${lastModified}
5741
- `;
5742
- return { data: text, annotations: { lastModified } };
5743
- }
5744
- };
5745
- var metricsUsersResource = {
5746
- uri: "memory://metrics/users",
5747
- name: "Metrics Users",
5748
- title: "Per-User Call Counts",
5749
- description: "Per-user tool call counts. Populated when user identifiers are provided via OAuth or request metadata. Returns empty breakdown when no user tracking configured.",
5750
- mimeType: "text/plain",
5751
- annotations: {
5752
- ...LOW_PRIORITY,
5753
- audience: ["assistant"]
5754
- },
5755
- handler: (_uri, ctx) => {
5756
- const lastModified = nowIso();
5757
- const userBreakdown = ctx.runtime?.metrics.getUserBreakdown() ?? {};
5758
- const users = Object.entries(userBreakdown);
5759
- if (users.length === 0) {
5760
- return {
5761
- data: `user_metrics:
5762
- note: No user tracking data available.
5763
- hint: User tracking activates when OAuth user identifiers are present.
5764
- as_of: ${lastModified}
5765
- `,
5766
- annotations: { lastModified }
5767
- };
5768
- }
5769
- const sorted = users.sort(([, a], [, b]) => b - a);
5770
- const rows = sorted.map(([user, count]) => ` - user: ${user}
5771
- calls: ${count}`).join("\n");
5772
- const text = `user_metrics:
5773
- as_of: ${lastModified}
5774
- ${rows}
5775
- `;
5776
- return { data: text, annotations: { lastModified } };
5777
- }
5778
- };
5779
- function getMetricsResourceDefinitions() {
5780
- return [
5781
- metricsSummaryResource,
5782
- metricsTokensResource,
5783
- metricsSystemResource,
5784
- metricsUsersResource
5785
- ];
5786
- }
5787
-
5788
- // src/handlers/resources/core/index.ts
5789
- function getCoreResourceDefinitions() {
5790
- return [
5791
- briefingResource,
5792
- dynamicBriefingResource,
5793
- instructionsResource,
5794
- recentResource,
5795
- significantResource,
5796
- tagsResource,
5797
- statisticsResource,
5798
- rulesResource,
5799
- workflowsResource,
5800
- skillsResource,
5801
- healthResource,
5802
- ...getMetricsResourceDefinitions()
5803
- ];
5804
- }
5805
-
5806
- // src/handlers/resources/graph.ts
5807
- function getGraphResourceDefinitions() {
5808
- return [
5809
- {
5810
- uri: "memory://graph/recent",
5811
- name: "Recent Relationship Graph",
5812
- title: "Live Mermaid Diagram",
5813
- description: "Live Mermaid diagram of recent relationships",
5814
- mimeType: "text/plain",
5815
- icons: [ICON_GRAPH],
5816
- annotations: MEDIUM_PRIORITY,
5817
- handler: (_uri, context) => {
5818
- const relationships = context.db.getRecentGraphRelationships(20);
5819
- if (relationships.length === 0) {
5820
- return 'graph TD\n NoData["No relationships found \u2014 use link_entries to create relationships"]';
5821
- }
5822
- const lines = ["graph TD"];
5823
- const seenNodes = /* @__PURE__ */ new Set();
5824
- const arrowStyles = {
5825
- references: "-->",
5826
- evolves_from: "-->",
5827
- depends_on: "-->",
5828
- implements: "==>",
5829
- resolved: "==>",
5830
- clarifies: "-..->",
5831
- caused: "-.->",
5832
- related_to: "<-->",
5833
- response_to: "<-->",
5834
- blocked_by: "--x"
5835
- };
5836
- for (const rel of relationships) {
5837
- if (!seenNodes.has(rel.from_entry_id)) {
5838
- const label = rel.from_content.slice(0, 30).replace(/[\]"'`[]]/g, " ").trim();
5839
- lines.push(
5840
- ` E${String(rel.from_entry_id)}["#${String(rel.from_entry_id)}: ${label}..."]`
5841
- );
5842
- seenNodes.add(rel.from_entry_id);
5843
- }
5844
- if (!seenNodes.has(rel.to_entry_id)) {
5845
- const label = rel.to_content.slice(0, 30).replace(/[\]"'`[]]/g, " ").trim();
5846
- lines.push(
5847
- ` E${String(rel.to_entry_id)}["#${String(rel.to_entry_id)}: ${label}..."]`
5848
- );
5849
- seenNodes.add(rel.to_entry_id);
5850
- }
5851
- const arrow = arrowStyles[rel.relationship_type] ?? "-->";
5852
- lines.push(
5853
- ` E${String(rel.from_entry_id)} ${arrow}|${rel.relationship_type}| E${String(rel.to_entry_id)}`
5854
- );
5855
- }
5856
- return lines.join("\n");
5857
- }
5858
- },
5859
- {
5860
- uri: "memory://graph/actions",
5861
- name: "Actions Graph",
5862
- title: "CI/CD Narrative Graph",
5863
- description: "CI/CD narrative graph: commits \u2192 runs \u2192 failures \u2192 entries \u2192 fixes \u2192 deployments",
5864
- mimeType: "text/plain",
5865
- icons: [ICON_GITHUB],
5866
- annotations: MEDIUM_PRIORITY,
5867
- handler: async (uri, context) => {
5868
- const match = /memory:\/\/graph\/actions\/?(.*)?/.exec(uri);
5869
- const targetRepo = match?.[1] ? decodeURIComponent(match[1]) : void 0;
5870
- const resolved = await resolveGitHubRepo(
5871
- context.github,
5872
- context.briefingConfig,
5873
- targetRepo,
5874
- context.runtime
5875
- );
5876
- if (isResourceError(resolved)) {
5877
- if (resolved.data !== null && typeof resolved.data === "object" && "error" in resolved.data && typeof resolved.data["error"] === "string") {
5878
- const errorMsg = resolved.data["error"];
5879
- if (errorMsg.includes("GitHub integration not available")) {
5880
- return 'graph LR\n NoGitHub["GitHub integration not available \u2014 set GITHUB_TOKEN"]';
5881
- }
5882
- }
5883
- return 'graph LR\n NoRepo["Repository not detected \u2014 run in valid git repo or use memory://graph/actions/{repo}"]';
5884
- }
5885
- const { owner, repo, github } = resolved;
5886
- const workflowRuns = await github.getWorkflowRuns(owner, repo, 10);
5887
- if (workflowRuns.length === 0) {
5888
- return 'graph LR\n NoRuns["No GitHub Actions workflow runs found for this repository"]';
5889
- }
5890
- const lines = ["graph LR"];
5891
- const statusStyles = {
5892
- success: ":::success",
5893
- failure: ":::failure",
5894
- cancelled: ":::cancelled",
5895
- skipped: ":::skipped"
5896
- };
5897
- lines.push(" classDef success fill:#28a745,color:#fff");
5898
- lines.push(" classDef failure fill:#dc3545,color:#fff");
5899
- lines.push(" classDef cancelled fill:#6c757d,color:#fff");
5900
- lines.push(" classDef skipped fill:#ffc107,color:#000");
5901
- for (const run of workflowRuns) {
5902
- const shortSha = run.headSha.slice(0, 7);
5903
- const nodeId = `R${String(run.id)}`;
5904
- const commitId = `C${shortSha}`;
5905
- const style = statusStyles[run.conclusion ?? "skipped"] ?? "";
5906
- const statusIcon = run.conclusion === "success" ? "\u2713" : run.conclusion === "failure" ? "\u2717" : "\u25CB";
5907
- lines.push(` ${commitId}["${shortSha}"]`);
5908
- lines.push(` ${nodeId}["${statusIcon} ${run.name}"]${style}`);
5909
- lines.push(` ${commitId} --> ${nodeId}`);
5910
- }
5911
- return lines.join("\n");
5912
- }
5913
- },
5914
- {
5915
- uri: "memory://actions/recent",
5916
- name: "Recent Actions",
5917
- title: "Recent Workflow Runs",
5918
- description: "Recent workflow runs with CI status",
5919
- mimeType: "application/json",
5920
- icons: [ICON_GITHUB],
5921
- annotations: ASSISTANT_FOCUSED,
5922
- handler: async (uri, context) => {
5923
- const match = /memory:\/\/actions\/recent\/?(.*)?/.exec(uri);
5924
- const targetRepo = match?.[1] ? decodeURIComponent(match[1]) : void 0;
5925
- try {
5926
- const resolved = await resolveGitHubRepo(
5927
- context.github,
5928
- context.briefingConfig,
5929
- targetRepo,
5930
- context.runtime
5931
- );
5932
- if (!isResourceError(resolved)) {
5933
- const { owner, repo, github } = resolved;
5934
- const runs = await github.getWorkflowRuns(owner, repo, 10);
5935
- const entries2 = runs.map((run) => ({
5936
- id: -1 * run.id,
5937
- entryType: "tool_output",
5938
- content: `Workflow: ${run.name}
5939
- Status: ${run.status}
5940
- Conclusion: ${run.conclusion || "pending"}
5941
- Branch: ${run.headBranch}
5942
- URL: ${run.url}`,
5943
- timestamp: run.createdAt,
5944
- isPersonal: false,
5945
- significanceType: null,
5946
- workflowRunId: run.id,
5947
- workflowName: run.name,
5948
- workflowStatus: run.conclusion || run.status
5949
- }));
5950
- return { entries: entries2, count: entries2.length, source: "github_api" };
5951
- }
5952
- } catch {
5953
- }
5954
- const entries = context.db.getWorkflowActionEntries(10);
5955
- return { entries, count: entries.length, source: "database" };
5956
- }
5957
- }
5958
- ];
5959
- }
5960
- function getDynamicGraphResourceDefinitions() {
5961
- const definitions = getGraphResourceDefinitions();
5962
- const dynamicDefinitions = [];
5963
- for (const def of definitions) {
5964
- if (def.uri === "memory://graph/actions" || def.uri === "memory://actions/recent") {
5965
- dynamicDefinitions.push({
5966
- ...def,
5967
- uri: def.uri + "/{+repo}",
5968
- name: def.name + " (Dynamic)",
5969
- description: def.description + " (Supports explicit multi-project repository targeting via {repo})"
5970
- });
5971
- }
5972
- }
5973
- return [...definitions, ...dynamicDefinitions];
5974
- }
5975
-
5976
- // src/handlers/resources/github.ts
5977
- var RESOURCE_ISSUE_LIMIT = 5;
5978
- var RESOURCE_PR_LIMIT = 5;
5979
- var RESOURCE_WORKFLOW_LIMIT = 5;
5980
- var RESOURCE_STATUS_MILESTONE_LIMIT = 5;
5981
- var RESOURCE_MILESTONE_LIMIT = 20;
5982
- function getGitHubResourceDefinitions() {
5983
- const definitions = [
5984
- {
5985
- uri: "memory://github/status",
5986
- name: "GitHub Status",
5987
- title: "GitHub Repository Status",
5988
- description: "Compact GitHub status: repository, branch, CI, issues, PRs, Kanban summary",
5989
- mimeType: "application/json",
5990
- icons: [ICON_GITHUB],
5991
- annotations: withPriority(0.7, ASSISTANT_FOCUSED),
5992
- handler: async (uri, context) => {
5993
- const match = /memory:\/\/github\/status\/?(.*)?/.exec(uri);
5994
- const targetRepo = match?.[1] ? decodeURIComponent(match[1]) : void 0;
5995
- const resolved = await resolveGitHubRepo(
5996
- context.github,
5997
- context.briefingConfig,
5998
- targetRepo,
5999
- context.runtime
6000
- );
6001
- if (isResourceError(resolved)) return resolved;
6002
- const { owner, repo, branch, lastModified, github } = resolved;
6003
- const defaultProjectNumber = context.briefingConfig?.defaultProjectNumber;
6004
- const withTimeout = (operation, ms, desc) => {
6005
- const controller = new AbortController();
6006
- return Promise.race([
6007
- operation(controller.signal),
6008
- new Promise(
6009
- (_, reject) => setTimeout(() => {
6010
- controller.abort();
6011
- reject(new Error(`GitHub API timeout: ${desc}`));
6012
- }, ms)
6013
- )
6014
- ]);
6015
- };
6016
- const [repoContextResult, kanbanResult] = await Promise.allSettled([
6017
- withTimeout((s) => github.getRepoContext(s), 1e4, "getRepoContext"),
6018
- defaultProjectNumber !== void 0 ? withTimeout(
6019
- (s) => github.getProjectKanban(owner, defaultProjectNumber, repo, s),
6020
- 1e4,
6021
- "getProjectKanban"
6022
- ) : Promise.resolve(null)
6023
- ]);
6024
- let commit = null;
6025
- let issues = [];
6026
- let prs = [];
6027
- let workflowRuns = [];
6028
- let milestonesContext = [];
6029
- if (repoContextResult.status === "fulfilled") {
6030
- commit = repoContextResult.value.commit;
6031
- issues = (repoContextResult.value.issues ?? []).slice(0, RESOURCE_ISSUE_LIMIT);
6032
- prs = (repoContextResult.value.pullRequests ?? []).slice(0, RESOURCE_PR_LIMIT);
6033
- workflowRuns = (repoContextResult.value.workflowRuns ?? []).slice(
6034
- 0,
6035
- RESOURCE_WORKFLOW_LIMIT
6036
- );
6037
- milestonesContext = (repoContextResult.value.milestones ?? []).slice(
6038
- 0,
6039
- RESOURCE_STATUS_MILESTONE_LIMIT
6040
- );
6041
- } else {
6042
- logger.debug("Failed to fetch repo context", {
6043
- module: "RESOURCE",
6044
- operation: "github-status",
6045
- error: repoContextResult.reason
6046
- });
6047
- }
6048
- const openIssues = issues.map((i) => ({
6049
- number: i.number,
6050
- title: markUntrustedContentInline(i.title.slice(0, 50))
6051
- }));
6052
- const openPrs = prs.map((pr) => ({
6053
- number: pr.number,
6054
- title: markUntrustedContentInline(pr.title.slice(0, 50)),
6055
- state: pr.state
6056
- }));
6057
- let ciStatus = "unknown";
6058
- let latestRun = null;
6059
- if (workflowRuns.length > 0) {
6060
- const latestCompleted = workflowRuns.find((r) => r.status === "completed");
6061
- const latest = workflowRuns[0];
6062
- latestRun = {
6063
- name: latest?.name ?? "Unknown",
6064
- conclusion: latest?.conclusion ?? null,
6065
- headSha: latest?.headSha?.slice(0, 7) ?? ""
6066
- };
6067
- if (latestCompleted) {
6068
- switch (latestCompleted.conclusion) {
6069
- case "success":
6070
- ciStatus = "passing";
6071
- break;
6072
- case "failure":
6073
- ciStatus = "failing";
6074
- break;
6075
- case "cancelled":
6076
- ciStatus = "cancelled";
6077
- break;
6078
- default:
6079
- ciStatus = "unknown";
6080
- }
6081
- } else if (workflowRuns.some((r) => r.status !== "completed")) {
6082
- ciStatus = "pending";
6083
- }
6084
- }
6085
- let kanbanSummary = null;
6086
- if (kanbanResult.status === "fulfilled" && kanbanResult.value) {
6087
- kanbanSummary = {};
6088
- for (const col of kanbanResult.value.columns) {
6089
- kanbanSummary[col.status] = col.items.length;
6090
- }
6091
- } else if (kanbanResult.status === "rejected") {
6092
- logger.debug("Failed to fetch Kanban board", {
6093
- module: "RESOURCE",
6094
- operation: "github-status",
6095
- error: kanbanResult.reason
6096
- });
6097
- }
6098
- let milestoneSummary;
6099
- if (repoContextResult.status === "fulfilled") {
6100
- milestoneSummary = {
6101
- openCount: milestonesContext.length,
6102
- items: milestonesContext.map((ms) => {
6103
- const pct = milestoneCompletionPct(ms.openIssues, ms.closedIssues);
6104
- return {
6105
- number: ms.number,
6106
- title: markUntrustedContentInline(ms.title),
6107
- state: ms.state,
6108
- openIssues: ms.openIssues,
6109
- closedIssues: ms.closedIssues,
6110
- completionPercentage: pct,
6111
- dueOn: ms.dueOn
6112
- };
6113
- })
6114
- };
6115
- } else {
6116
- milestoneSummary = null;
6117
- logger.debug("Failed to fetch milestones from context", {
6118
- module: "RESOURCE",
6119
- operation: "github-status",
6120
- error: repoContextResult.reason
6121
- });
6122
- }
6123
- let fetchStatus = "ok";
6124
- const failures = [];
6125
- if (repoContextResult.status === "rejected") failures.push("context");
6126
- if (kanbanResult.status === "rejected") failures.push("kanban");
6127
- if (failures.length > 0) {
6128
- const totalAttempts = defaultProjectNumber !== void 0 ? 2 : 1;
6129
- if (failures.length === totalAttempts) {
6130
- fetchStatus = "failed";
6131
- } else {
6132
- fetchStatus = "degraded";
6133
- }
6134
- }
6135
- return {
6136
- data: {
6137
- status: fetchStatus,
6138
- failures: failures.length > 0 ? failures : void 0,
6139
- repository: `${owner}/${repo}`,
6140
- branch,
6141
- commit: commit?.slice(0, 7) ?? null,
6142
- ci: {
6143
- status: ciStatus,
6144
- latestRun
6145
- },
6146
- issues: {
6147
- openCount: issues.length,
6148
- items: openIssues
6149
- },
6150
- pullRequests: {
6151
- openCount: prs.length,
6152
- items: openPrs
6153
- },
6154
- kanbanSummary,
6155
- milestones: milestoneSummary
6156
- },
6157
- annotations: { lastModified }
6158
- };
6159
- }
6160
- },
6161
- // Repository insights resource
6162
- {
6163
- uri: "memory://github/insights",
6164
- name: "Repository Insights",
6165
- title: "Repository Stars & Traffic Summary",
6166
- description: "Compact repo insights: stars, forks, 14-day traffic totals (~150 tokens)",
6167
- mimeType: "application/json",
6168
- icons: [ICON_ANALYTICS],
6169
- annotations: { ...LOW_PRIORITY, audience: ["assistant"] },
6170
- handler: async (uri, context) => {
6171
- const match = /memory:\/\/github\/insights\/?(.*)?/.exec(uri);
6172
- const targetRepo = match?.[1] ? decodeURIComponent(match[1]) : void 0;
6173
- const resolved = await resolveGitHubRepo(
6174
- context.github,
6175
- context.briefingConfig,
6176
- targetRepo,
6177
- context.runtime
6178
- );
6179
- if (isResourceError(resolved)) return resolved;
6180
- const { owner, repo, lastModified, github } = resolved;
6181
- const stats = await github.getRepoStats(owner, repo);
6182
- let traffic = null;
6183
- try {
6184
- const trafficData = await github.getTrafficData(owner, repo);
6185
- if (trafficData) {
6186
- traffic = {
6187
- clones14d: trafficData.clones.total,
6188
- views14d: trafficData.views.total
6189
- };
6190
- }
6191
- } catch {
6192
- }
6193
- return {
6194
- data: {
6195
- repository: `${owner}/${repo}`,
6196
- stars: stats?.stars ?? null,
6197
- forks: stats?.forks ?? null,
6198
- watchers: stats?.watchers ?? null,
6199
- ...traffic ?? {},
6200
- hint: !traffic ? "Traffic data requires push access to the repository." : void 0
6201
- },
6202
- annotations: { lastModified }
6203
- };
6204
- }
6205
- },
6206
- // Milestone resources
6207
- {
6208
- uri: "memory://github/milestones",
6209
- name: "GitHub Milestones",
6210
- title: "GitHub Repository Milestones",
6211
- description: "Open GitHub milestones with completion percentages, due dates, and issue counts",
6212
- mimeType: "application/json",
6213
- icons: [ICON_MILESTONE],
6214
- annotations: { ...MEDIUM_PRIORITY, audience: ["assistant"] },
6215
- handler: async (uri, context) => {
6216
- const match = /memory:\/\/github\/milestones\/?(.*)?/.exec(uri);
6217
- const targetRepo = match?.[1] ? decodeURIComponent(match[1]) : void 0;
6218
- const resolved = await resolveGitHubRepo(
6219
- context.github,
6220
- context.briefingConfig,
6221
- targetRepo,
6222
- context.runtime
6223
- );
6224
- if (isResourceError(resolved)) return resolved;
6225
- const { owner, repo, lastModified, github } = resolved;
6226
- const milestones = await github.getMilestones(
6227
- owner,
6228
- repo,
6229
- "open",
6230
- RESOURCE_MILESTONE_LIMIT
6231
- );
6232
- const milestonesWithProgress = milestones.map((ms) => {
6233
- const completionPercentage = milestoneCompletionPct(
6234
- ms.openIssues,
6235
- ms.closedIssues
6236
- );
6237
- return {
6238
- ...ms,
6239
- title: markUntrustedContentInline(ms.title),
6240
- completionPercentage
6241
- };
6242
- });
6243
- return {
6244
- data: {
6245
- repository: `${owner}/${repo}`,
6246
- milestones: milestonesWithProgress,
6247
- count: milestonesWithProgress.length,
6248
- hint: "Use get_github_milestones tool for state filtering. Use memory://milestones/{number} for detail."
6249
- },
6250
- annotations: { lastModified }
6251
- };
6252
- }
6253
- },
6254
- {
6255
- uri: "memory://milestones/{number}",
6256
- name: "Milestone Detail",
6257
- title: "GitHub Milestone Detail",
6258
- description: "Detailed view of a single GitHub milestone with completion progress and issue counts. Use get_github_issues with the milestone filter for individual issue details.",
6259
- mimeType: "application/json",
6260
- icons: [ICON_MILESTONE],
6261
- annotations: ASSISTANT_FOCUSED,
6262
- handler: async (uri, context) => {
6263
- const lastModified = (/* @__PURE__ */ new Date()).toISOString();
6264
- const match = /memory:\/\/milestones\/(?:(.*)\/)?(\d+)$/.exec(uri);
6265
- const targetRepo = match?.[1] ? decodeURIComponent(match[1]) : void 0;
6266
- const milestoneNumber = match?.[2] ? parseInt(match[2], 10) : null;
6267
- if (milestoneNumber === null) {
6268
- return {
6269
- data: { error: "Invalid milestone number" },
6270
- annotations: { lastModified }
6271
- };
6272
- }
6273
- const resolved = await resolveGitHubRepo(
6274
- context.github,
6275
- context.briefingConfig,
6276
- targetRepo,
6277
- context.runtime
6278
- );
6279
- if (isResourceError(resolved)) return resolved;
6280
- const { owner, repo, github } = resolved;
6281
- const milestone = await github.getMilestone(owner, repo, milestoneNumber);
6282
- if (!milestone) {
6283
- return {
6284
- data: { error: `Milestone #${String(milestoneNumber)} not found` },
6285
- annotations: { lastModified }
6286
- };
6287
- }
6288
- const completionPercentage = milestoneCompletionPct(
6289
- milestone.openIssues,
6290
- milestone.closedIssues
6291
- );
6292
- return {
6293
- data: {
6294
- repository: `${owner}/${repo}`,
6295
- milestone: {
6296
- ...milestone,
6297
- title: markUntrustedContentInline(milestone.title),
6298
- completionPercentage
6299
- },
6300
- hint: "Use get_github_issues tool to list issues associated with this milestone."
6301
- },
6302
- annotations: { lastModified }
6303
- };
6304
- }
6305
- }
6306
- ];
6307
- const dynamicDefinitions = definitions.map((def) => {
6308
- const dynamicName = def.name + " (Dynamic)";
6309
- let dynamicUri;
6310
- if (def.uri === "memory://milestones/{number}") {
6311
- dynamicUri = "memory://milestones/{+repo}/{number}";
6312
- } else {
6313
- dynamicUri = def.uri + "/{+repo}";
6314
- }
6315
- return {
6316
- ...def,
6317
- uri: dynamicUri,
6318
- name: dynamicName,
6319
- description: def.description + " (Supports explicit multi-project repository targeting via {repo})"
6320
- };
6321
- });
6322
- return [...definitions, ...dynamicDefinitions];
6323
- }
6324
-
6325
- // src/handlers/resources/templates.ts
6326
- function getTemplateResourceDefinitions() {
6327
- const definitions = [
6328
- {
6329
- uri: "memory://projects/{number}/timeline",
6330
- name: "Project Timeline",
6331
- title: "Project Activity Timeline",
6332
- description: "Project activity timeline",
6333
- mimeType: "application/json",
6334
- annotations: { ...MEDIUM_PRIORITY, audience: ["assistant"] },
6335
- handler: (uri, context) => {
6336
- const match = /memory:\/\/projects\/(\d+)\/timeline/.exec(uri);
6337
- const projectNumber = match?.[1] ? parseInt(match[1], 10) : null;
6338
- if (projectNumber === null) {
6339
- return { error: "Invalid project number" };
6340
- }
6341
- const entries = context.db.searchEntries("", { projectNumber, limit: 50 });
6342
- return { projectNumber, entries, count: entries.length };
6343
- }
6344
- },
6345
- {
6346
- uri: "memory://issues/{issue_number}/entries",
6347
- name: "Issue Entries",
6348
- title: "Entries Linked to Issue",
6349
- description: "All entries linked to a specific issue",
6350
- mimeType: "application/json",
6351
- icons: [ICON_ISSUE],
6352
- annotations: { ...MEDIUM_PRIORITY, audience: ["assistant"] },
6353
- handler: (uri, context) => {
6354
- const match = /memory:\/\/issues\/(\d+)\/entries/.exec(uri);
6355
- const issueNumber = match?.[1] ? parseInt(match[1], 10) : null;
6356
- if (issueNumber === null) {
6357
- return { error: "Invalid issue number" };
6358
- }
6359
- const entries = context.db.searchEntries("", { issueNumber, limit: 100 });
6360
- return { issueNumber, entries, count: entries.length };
6361
- }
6362
- },
6363
- {
6364
- uri: "memory://prs/{pr_number}/entries",
6365
- name: "PR Entries",
6366
- title: "Entries Linked to PR",
6367
- description: "All entries linked to a specific pull request",
6368
- mimeType: "application/json",
6369
- icons: [ICON_PR],
6370
- annotations: { ...MEDIUM_PRIORITY, audience: ["assistant"] },
6371
- handler: (uri, context) => {
6372
- const match = /memory:\/\/prs\/(\d+)\/entries/.exec(uri);
6373
- const prNumber = match?.[1] ? parseInt(match[1], 10) : null;
6374
- if (prNumber === null) {
6375
- return { error: "Invalid PR number" };
6376
- }
6377
- const entries = context.db.searchEntries("", { prNumber, limit: 100 });
6378
- return {
6379
- prNumber,
6380
- entries,
6381
- count: entries.length,
6382
- ...entries.length === 0 ? {
6383
- hint: "No journal entries linked to this PR. Use create_entry with pr_number to link entries."
6384
- } : {}
6385
- };
6386
- }
6387
- },
6388
- {
6389
- uri: "memory://prs/{pr_number}/timeline",
6390
- name: "PR Timeline",
6391
- title: "Combined PR and Journal Timeline",
6392
- description: "Combined PR + journal timeline with live PR metadata",
6393
- mimeType: "application/json",
6394
- icons: [ICON_PR],
6395
- annotations: ASSISTANT_FOCUSED,
6396
- handler: async (uri, context) => {
6397
- const match = /memory:\/\/prs\/(\d+)\/timeline/.exec(uri);
6398
- const prNumber = match?.[1] ? parseInt(match[1], 10) : null;
6399
- if (prNumber === null) {
6400
- return { error: "Invalid PR number" };
6401
- }
6402
- let prMetadata = null;
6403
- if (context.github) {
6404
- try {
6405
- const repoInfo = await context.github.getRepoInfo();
6406
- if (repoInfo.owner && repoInfo.repo) {
6407
- const pr = await context.github.getPullRequest(
6408
- repoInfo.owner,
6409
- repoInfo.repo,
6410
- prNumber
6411
- );
6412
- if (pr) {
6413
- prMetadata = {
6414
- title: pr.title,
6415
- state: pr.state,
6416
- draft: pr.draft,
6417
- mergedAt: pr.mergedAt,
6418
- closedAt: pr.closedAt,
6419
- author: pr.author,
6420
- headBranch: pr.headBranch,
6421
- baseBranch: pr.baseBranch
6422
- };
6423
- }
6424
- }
6425
- } catch {
6426
- }
6427
- }
6428
- const entries = context.db.searchEntries("", { prNumber, limit: 100 });
6429
- let timelineNote;
6430
- if (prMetadata) {
6431
- const stateDesc = prMetadata.state.toLowerCase();
6432
- const mergedNote = prMetadata.mergedAt ? " (merged)" : "";
6433
- const draftNote = prMetadata.draft ? " [DRAFT]" : "";
6434
- timelineNote = `PR #${String(prNumber)} is ${stateDesc}${mergedNote}${draftNote}`;
6435
- } else {
6436
- timelineNote = "GitHub integration unavailable for live PR status. Entry timestamps show journal activity.";
6437
- }
6438
- return {
6439
- prNumber,
6440
- prMetadata,
6441
- entries,
6442
- count: entries.length,
6443
- timelineNote,
6444
- ...entries.length === 0 ? {
6445
- hint: "No journal entries linked to this PR. Use create_entry with pr_number to link entries."
6446
- } : {}
6447
- };
6448
- }
6449
- },
6450
- // Kanban board resources (GitHub Projects v2)
6451
- {
6452
- uri: "memory://kanban/{project_number}",
6453
- name: "Kanban Board",
6454
- title: "GitHub Project Kanban Board",
6455
- description: "View a GitHub Project v2 as a Kanban board with items grouped by Status",
6456
- mimeType: "application/json",
6457
- annotations: { ...MEDIUM_PRIORITY, audience: ["assistant"] },
6458
- handler: async (uri, context) => {
6459
- const match = /memory:\/\/kanban\/(?:(.*)\/)?(\d+)$/.exec(uri);
6460
- const targetRepo = match?.[1] ? decodeURIComponent(match[1]) : void 0;
6461
- const projectNumber = match?.[2] ? parseInt(match[2], 10) : null;
6462
- if (projectNumber === null) {
6463
- return { error: "Invalid project number" };
6464
- }
6465
- const resolved = await resolveGitHubRepo(
6466
- context.github,
6467
- context.briefingConfig,
6468
- targetRepo,
6469
- context.runtime
6470
- );
6471
- if (isResourceError(resolved)) return resolved.data;
6472
- const { owner, repo, github } = resolved;
6473
- const board = await github.getProjectKanban(owner, projectNumber, repo);
6474
- if (!board) {
6475
- return {
6476
- error: `Project #${String(projectNumber)} not found or Status field not configured`,
6477
- projectNumber,
6478
- owner,
6479
- hint: "Projects can be at user, repository, or organization level."
6480
- };
6481
- }
6482
- for (const column of board.columns) {
6483
- for (const item of column.items) {
6484
- if ("body" in item) {
6485
- delete item.body;
6486
- }
6487
- }
6488
- }
6489
- return board;
6490
- }
6491
- },
6492
- {
6493
- uri: "memory://kanban/{project_number}/diagram",
6494
- name: "Kanban Diagram",
6495
- title: "Kanban Board Mermaid Diagram",
6496
- description: "Mermaid diagram visualization of a GitHub Project Kanban board",
6497
- mimeType: "text/plain",
6498
- annotations: MEDIUM_PRIORITY,
6499
- handler: async (uri, context) => {
6500
- const match = /memory:\/\/kanban\/(?:(.*)\/)?(\d+)\/diagram$/.exec(uri);
6501
- const targetRepo = match?.[1] ? decodeURIComponent(match[1]) : void 0;
6502
- const projectNumber = match?.[2] ? parseInt(match[2], 10) : null;
6503
- if (projectNumber === null) {
6504
- return { error: "Invalid project number" };
6505
- }
6506
- const resolved = await resolveGitHubRepo(
6507
- context.github,
6508
- context.briefingConfig,
6509
- targetRepo,
6510
- context.runtime
6511
- );
6512
- if (isResourceError(resolved)) {
6513
- const errObj = resolved.data;
6514
- return `graph LR
6515
- Error["${errObj.error.replace(/["[\]]/g, "'")} \u2014 ${errObj.hint ? errObj.hint.replace(/["[\]]/g, "'") : ""}"]`;
6516
- }
6517
- const { owner, repo, github } = resolved;
6518
- const board = await github.getProjectKanban(owner, projectNumber, repo);
6519
- if (!board) {
6520
- return `graph LR
6521
- NotFound["Project #${String(projectNumber)} not found \u2014 ensure project exists with a Status field"]`;
6522
- }
6523
- const lines = ["graph LR"];
6524
- lines.push(" classDef issue fill:#28a745,color:#fff");
6525
- lines.push(" classDef pr fill:#6f42c1,color:#fff");
6526
- lines.push(" classDef draft fill:#6c757d,color:#fff");
6527
- for (const column of board.columns) {
6528
- const safeStatus = column.status.replace(/["\s]/g, "_");
6529
- lines.push(
6530
- ` subgraph ${safeStatus}["${column.status} (${String(column.items.length)})"]`
6531
- );
6532
- for (const item of column.items) {
6533
- const safeId = item.id.replace(/[^a-zA-Z0-9]/g, "").slice(-8);
6534
- const label = item.title.slice(0, 25).replace(/["[\]]/g, "'");
6535
- const typeIcon = item.type === "ISSUE" ? "\u{1F535}" : item.type === "PULL_REQUEST" ? "\u{1F7E3}" : "\u26AA";
6536
- const numberStr = item.number !== void 0 && item.number !== 0 ? `#${String(item.number)}` : "";
6537
- lines.push(` I${safeId}["${typeIcon} ${numberStr} ${label}..."]`);
6538
- const typeClass = item.type === "ISSUE" ? "issue" : item.type === "PULL_REQUEST" ? "pr" : "draft";
6539
- lines.push(` class I${safeId} ${typeClass}`);
6540
- }
6541
- lines.push(" end");
6542
- }
6543
- return lines.join("\n");
6544
- }
6545
- }
6546
- ];
6547
- const dynamicDefinitions = [];
6548
- for (const def of definitions) {
6549
- if (def.uri.startsWith("memory://kanban/")) {
6550
- const dynamicUri = def.uri.replace("{project_number}", "{+repo}/{project_number}");
6551
- dynamicDefinitions.push({
6552
- ...def,
6553
- uri: dynamicUri,
6554
- name: def.name + " (Dynamic)",
6555
- description: def.description + " (Supports explicit multi-project repository targeting via {repo})"
6556
- });
6557
- }
6558
- }
6559
- return [...definitions, ...dynamicDefinitions];
6560
- }
6561
-
6562
- // src/handlers/resources/team.ts
6563
- function enrichWithAuthor(entries, context) {
6564
- const teamDb = context.teamDb;
6565
- if (!teamDb || entries.length === 0) return entries.map((e) => ({ ...e, author: null }));
6566
- const ids = entries.map((e) => e.id);
6567
- const authorMap = teamDb.getAuthorsForEntries(ids);
6568
- return entries.map((e) => ({
6569
- ...e,
6570
- author: authorMap.get(e.id) ?? null
6571
- }));
6572
- }
6573
- function getTeamResourceDefinitions() {
6574
- const resources = [
6575
- {
6576
- uri: "memory://team/recent",
6577
- name: "Recent Team Entries",
6578
- title: "Recent Team-Shared Entries",
6579
- description: "Recent entries from the team database. Requires TEAM_DB_PATH configuration.",
6580
- mimeType: "application/json",
6581
- icons: [ICON_CLOCK],
6582
- annotations: withPriority(0.7, ASSISTANT_FOCUSED),
6583
- handler: (_uri, context) => {
6584
- if (!context.teamDb) {
6585
- return {
6586
- data: {
6587
- error: "Team database not configured. Set TEAM_DB_PATH to enable.",
6588
- entries: [],
6589
- count: 0
6590
- }
6591
- };
6592
- }
6593
- const entries = context.teamDb.getRecentEntries(10);
6594
- const lastModified = entries[0]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
6595
- const enriched = enrichWithAuthor(entries, context);
6596
- return {
6597
- data: {
6598
- entries: enriched.map((e) => ({
6599
- ...e,
6600
- content: markUntrustedContent(e.content),
6601
- author: e.author ? sanitizeAuthor(e.author) : null
6602
- })),
6603
- count: enriched.length,
6604
- source: "team"
6605
- },
6606
- annotations: { lastModified }
6607
- };
6608
- }
6609
- },
6610
- {
6611
- uri: "memory://team/statistics",
6612
- name: "Team Statistics",
6613
- title: "Team Database Statistics",
6614
- description: "Entry counts, types, and contributor breakdown for the team database.",
6615
- mimeType: "application/json",
6616
- icons: [ICON_TEAM],
6617
- annotations: { ...MEDIUM_PRIORITY, audience: ["assistant"] },
6618
- handler: (_uri, context) => {
6619
- if (!context.teamDb) {
6620
- return {
6621
- data: {
6622
- error: "Team database not configured. Set TEAM_DB_PATH to enable.",
6623
- configured: false
6624
- }
6625
- };
6626
- }
6627
- const stats = context.teamDb.getStatistics("week");
6628
- let authors = [];
6629
- try {
6630
- authors = context.teamDb.getAuthorStatistics();
6631
- } catch (error) {
6632
- logger.warning("Failed to get team author statistics", {
6633
- module: "ResourceHandler",
6634
- error: error instanceof Error ? error.message : "Unknown error"
6635
- });
6636
- }
6637
- return {
6638
- data: {
6639
- configured: true,
6640
- ...stats,
6641
- authors,
6642
- source: "team"
6643
- }
6644
- };
6645
- }
6646
- },
6647
- // ====================================================================
6648
- // Flag Resources (Hush Protocol)
6649
- // ====================================================================
6650
- {
6651
- uri: "memory://flags",
6652
- name: "Active Flags",
6653
- title: "Active Team Flags Dashboard",
6654
- description: "Active (unresolved) flags from the Hush Protocol. Shows machine-actionable developer signals that need attention. Requires TEAM_DB_PATH.",
6655
- mimeType: "application/json",
6656
- icons: [ICON_FLAG],
6657
- annotations: withPriority(0.8, ASSISTANT_FOCUSED),
6658
- handler: (_uri, context) => {
6659
- if (!context.teamDb) {
6660
- return {
6661
- data: {
6662
- error: "Team database not configured. Set TEAM_DB_PATH to enable.",
6663
- activeFlags: [],
6664
- count: 0
6665
- }
6666
- };
6667
- }
6668
- const flagEntries = context.teamDb.searchEntries("", {
6669
- entryType: "flag",
6670
- limit: 100
6671
- });
6672
- const enriched = enrichWithAuthor(flagEntries, context);
6673
- const activeFlags = enriched.map((entry) => {
6674
- const flagCtx = parseFlagContext(entry.autoContext);
6675
- if (!flagCtx || flagCtx.resolved) return null;
6676
- return {
6677
- id: entry.id,
6678
- flag_type: flagCtx.flag_type,
6679
- target_user: flagCtx.target_user,
6680
- link: flagCtx.link,
6681
- author: entry.author ? sanitizeAuthor(entry.author) : null,
6682
- timestamp: entry.timestamp,
6683
- preview: markUntrustedContent(
6684
- entry.content.slice(0, 120) + (entry.content.length > 120 ? "..." : "")
6685
- ),
6686
- tags: entry.tags,
6687
- projectNumber: entry.projectNumber ?? null
6688
- };
6689
- }).filter((f) => f !== null);
6690
- const lastModified = activeFlags[0]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
6691
- return {
6692
- data: {
6693
- activeFlags,
6694
- count: activeFlags.length
6695
- },
6696
- annotations: { lastModified }
6697
- };
6698
- }
6699
- },
6700
- {
6701
- uri: "memory://flags/vocabulary",
6702
- name: "Flag Vocabulary",
6703
- title: "Hush Protocol Flag Vocabulary",
6704
- description: "Returns the configured flag vocabulary for the Hush Protocol. Static resource reflecting server-wide configuration.",
6705
- mimeType: "application/json",
6706
- icons: [ICON_FLAG],
6707
- annotations: { ...MEDIUM_PRIORITY, audience: ["assistant"] },
6708
- handler: (_uri, context) => {
6709
- const config = context.briefingConfig;
6710
- const custom = config?.flagVocabulary;
6711
- const hasCustom = Array.isArray(custom) && custom.length > 0;
6712
- const vocabulary = hasCustom ? custom.map(String) : [...DEFAULT_FLAG_VOCABULARY];
6713
- return {
6714
- data: {
6715
- vocabulary,
6716
- count: vocabulary.length,
6717
- isDefault: !hasCustom
6718
- }
6719
- };
6720
- }
6721
- }
6722
- ];
6723
- return resources.map((resource) => ({
6724
- ...resource,
6725
- capabilities: {
6726
- ...resource.capabilities,
6727
- requiresTeamScope: true
6728
- }
6729
- }));
6730
- }
6731
-
6732
- // src/handlers/resources/help.ts
6733
- var GROUP_DESCRIPTIONS = {
6734
- core: "Create, read, update, delete journal entries and manage recent activity",
6735
- search: "Full-text search, semantic search, date range queries, and vector index stats",
6736
- analytics: "Entry analytics, importance scoring, and productivity trends",
6737
- relationships: "Link and visualize relationships between journal entries",
6738
- io: "Import and export journal data \u2014 JSON export, Markdown round-trip (export/import)",
6739
- admin: "Update entries, rebuild indexes, and manage the vector search index",
6740
- github: "GitHub integration \u2014 issues, PRs, milestones, workflow runs, Kanban boards",
6741
- backup: "Create and restore database backups",
6742
- team: "Team collaboration \u2014 shared entries, cross-project insights, team analytics",
6743
- codemode: "Sandboxed JavaScript execution with access to the journal API"
6744
- };
6745
- var ZOD_TYPE_DISPLAY = {
6746
- ZodString: "string",
6747
- ZodNumber: "number",
6748
- ZodBoolean: "boolean",
6749
- ZodArray: "array",
6750
- ZodObject: "object",
6751
- ZodEnum: "enum",
6752
- ZodNativeEnum: "enum",
6753
- ZodLiteral: "literal",
6754
- ZodUnion: "union",
6755
- ZodIntersection: "intersection",
6756
- ZodRecord: "record",
6757
- ZodTuple: "tuple",
6758
- ZodAny: "any",
6759
- ZodUnknown: "unknown",
6760
- ZodNull: "null",
6761
- ZodUndefined: "undefined",
6762
- ZodVoid: "void"
6763
- };
6764
- var ZOD_OPTIONAL_WRAPPERS = /* @__PURE__ */ new Set([
6765
- "ZodOptional",
6766
- "ZodDefault",
6767
- "ZodNullable",
6768
- "optional",
6769
- "default",
6770
- "nullable"
6771
- ]);
6772
- function peelZodType(def) {
6773
- let isOptional = false;
6774
- let description;
6775
- let current = def;
6776
- for (; ; ) {
6777
- if (description === void 0 && typeof current["description"] === "string") {
6778
- description = current["description"];
6779
- }
6780
- const tn = current["typeName"] ?? current["type"];
6781
- if (!tn) break;
6782
- if (ZOD_OPTIONAL_WRAPPERS.has(tn)) {
6783
- isOptional = true;
6784
- const inner = current["innerType"];
6785
- if (!inner?._def) break;
6786
- current = inner._def;
6787
- } else {
6788
- const normalized = tn.replace("Zod", "").toLowerCase();
6789
- const displayName = ZOD_TYPE_DISPLAY[tn] ?? normalized;
6790
- return { typeName: displayName, isOptional, description };
6791
- }
6792
- }
6793
- return { typeName: "unknown", isOptional, description };
6794
- }
6795
- function extractParameters(inputSchema) {
6796
- if (inputSchema === void 0 || inputSchema === null || typeof inputSchema !== "object") {
6797
- return [];
6798
- }
6799
- const schema = inputSchema;
6800
- const rawShape = schema["shape"];
6801
- if (rawShape === void 0 || rawShape === null || typeof rawShape !== "object") {
6802
- return [];
6803
- }
6804
- const shape = rawShape;
6805
- const params = [];
6806
- for (const [name, fieldSchema] of Object.entries(shape)) {
6807
- if (fieldSchema === void 0 || fieldSchema === null || typeof fieldSchema !== "object") {
6808
- continue;
6809
- }
6810
- const field = fieldSchema;
6811
- const rawDef = field["_def"];
6812
- if (!rawDef) continue;
6813
- const { typeName, isOptional, description } = peelZodType(rawDef);
6814
- params.push({
6815
- name,
6816
- type: typeName,
6817
- required: !isOptional,
6818
- ...description !== void 0 ? { description } : {}
6819
- });
6820
- }
6821
- return params;
6822
- }
6823
- function getHelpResourceDefinitions() {
6824
- return [
6825
- {
6826
- uri: "memory://help",
6827
- name: "Help \u2014 Tool Groups",
6828
- title: "Tool Group Overview",
6829
- description: "Lists all tool groups with tool counts and descriptions. Read memory://help/{group} for per-tool details.",
6830
- mimeType: "application/json",
6831
- icons: [ICON_BRIEFING],
6832
- annotations: ASSISTANT_FOCUSED,
6833
- handler: async (_uri, context) => {
6834
- const tools = await getAllToolDefinitionsAsync(context);
6835
- const groups = /* @__PURE__ */ new Map();
6836
- for (const tool of tools) {
6837
- const existing = groups.get(tool.group);
6838
- if (existing) {
6839
- existing.names.push(tool.name);
6840
- if (!tool.annotations?.readOnlyHint) existing.readOnly = false;
6841
- } else {
6842
- groups.set(tool.group, {
6843
- names: [tool.name],
6844
- readOnly: tool.annotations?.readOnlyHint ?? false
6845
- });
6846
- }
6847
- }
6848
- const groupList = Array.from(groups.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([name, info]) => ({
6849
- name,
6850
- description: GROUP_DESCRIPTIONS[name] ?? name,
6851
- toolCount: info.names.length,
6852
- readOnly: info.readOnly,
6853
- tools: info.names.sort(),
6854
- helpUri: `memory://help/${name}`
6855
- }));
6856
- return {
6857
- data: {
6858
- totalTools: tools.length,
6859
- totalGroups: groupList.length,
6860
- groups: groupList,
6861
- gotchas: "memory://help/gotchas",
6862
- hint: "Read memory://help/{group} for detailed parameter info on each tool. Read memory://help/gotchas for field notes and critical usage patterns."
6863
- }
6864
- };
6865
- }
6866
- },
6867
- {
6868
- uri: "memory://help/{group}",
6869
- name: "Help \u2014 Tool Group Detail",
6870
- title: "Per-Group Tool Reference",
6871
- description: "Detailed tool reference for a specific group with parameters and annotations.",
6872
- mimeType: "application/json",
6873
- icons: [ICON_BRIEFING],
6874
- annotations: ASSISTANT_FOCUSED,
6875
- handler: async (uri, context) => {
6876
- const match = /memory:\/\/help\/([a-z]+)/.exec(uri);
6877
- const groupName = match?.[1];
6878
- if (!groupName) {
6879
- return {
6880
- data: {
6881
- error: "Invalid group name in URI",
6882
- hint: "Read memory://help for a list of available groups."
6883
- }
6884
- };
6885
- }
6886
- const tools = await getAllToolDefinitionsAsync(context);
6887
- const groupTools = tools.filter((t) => t.group === groupName);
6888
- if (groupTools.length === 0) {
6889
- const availableGroups = [...new Set(tools.map((t) => t.group))].sort();
6890
- return {
6891
- data: {
6892
- error: `Group "${groupName}" not found`,
6893
- availableGroups,
6894
- hint: "Read memory://help for a list of available groups."
6895
- }
6896
- };
6897
- }
6898
- const toolDetails = groupTools.map((tool) => ({
6899
- name: tool.name,
6900
- title: tool.title,
6901
- description: tool.description,
6902
- parameters: extractParameters(tool.inputSchema),
6903
- annotations: {
6904
- readOnly: tool.annotations?.readOnlyHint ?? false,
6905
- destructive: tool.annotations?.destructiveHint ?? false,
6906
- idempotent: tool.annotations?.idempotentHint ?? false,
6907
- openWorld: tool.annotations?.openWorldHint ?? false
6908
- },
6909
- hasOutputSchema: tool.outputSchema !== void 0
6910
- }));
6911
- return {
6912
- data: {
6913
- group: groupName,
6914
- description: GROUP_DESCRIPTIONS[groupName] ?? groupName,
6915
- toolCount: toolDetails.length,
6916
- tools: toolDetails
6917
- }
6918
- };
6919
- }
6920
- },
6921
- {
6922
- uri: "memory://help/gotchas",
6923
- name: "Help \u2014 Field Notes & Gotchas",
6924
- title: "Critical Usage Patterns",
6925
- description: "Field notes, edge cases, and critical usage patterns for memory-journal-mcp tools.",
6926
- mimeType: "text/markdown",
6927
- icons: [ICON_BRIEFING],
6928
- annotations: ASSISTANT_FOCUSED,
6929
- handler: () => {
6930
- return {
6931
- data: GOTCHAS_CONTENT
6932
- };
6933
- }
6934
- }
6935
- ];
6936
- }
6937
- var toolIndexModule = null;
6938
- async function getAllToolDefinitionsAsync(context) {
6939
- try {
6940
- toolIndexModule ??= await import('./tools-P4VGG4FH.js');
6941
- if (toolIndexModule === null) return [];
6942
- const tools = toolIndexModule.getTools(context.db, null);
6943
- return tools.map((t) => ({
6944
- name: t.name,
6945
- title: t.title ?? t.name,
6946
- description: t.description,
6947
- group: inferGroupFromName(t.name),
6948
- inputSchema: t.inputSchema,
6949
- outputSchema: t.outputSchema,
6950
- annotations: t.annotations
6951
- }));
6952
- } catch (e) {
6953
- logger.error("HELP_LOAD_TOOLS_FAILED", {
6954
- error: e instanceof Error ? e.message : String(e)
6955
- });
6956
- return [];
6957
- }
6958
- }
6959
- function inferGroupFromName(name) {
6960
- if (name.startsWith("team_")) return "team";
6961
- if (name.startsWith("mj_")) return "codemode";
6962
- const groupMap = {
6963
- // core (6)
6964
- create_entry: "core",
6965
- create_entry_minimal: "core",
6966
- get_entry_by_id: "core",
6967
- get_recent_entries: "core",
6968
- test_simple: "core",
6969
- list_tags: "core",
6970
- // search (4)
6971
- search_entries: "search",
6972
- search_by_date_range: "search",
6973
- semantic_search: "search",
6974
- get_vector_index_stats: "search",
6975
- // analytics (2)
6976
- get_statistics: "analytics",
6977
- get_cross_project_insights: "analytics",
6978
- // relationships (2)
6979
- link_entries: "relationships",
6980
- visualize_relationships: "relationships",
6981
- // io (3)
6982
- export_entries: "io",
6983
- export_markdown: "io",
6984
- import_markdown: "io",
6985
- // admin (5)
6986
- update_entry: "admin",
6987
- delete_entry: "admin",
6988
- merge_tags: "admin",
6989
- rebuild_vector_index: "admin",
6990
- add_to_vector_index: "admin",
6991
- // backup (4)
6992
- backup_journal: "backup",
6993
- list_backups: "backup",
6994
- restore_backup: "backup",
6995
- cleanup_backups: "backup",
6996
- // github (16)
6997
- get_github_issues: "github",
6998
- get_github_prs: "github",
6999
- get_github_issue: "github",
7000
- get_github_pr: "github",
7001
- get_github_context: "github",
7002
- get_github_milestones: "github",
7003
- get_github_milestone: "github",
7004
- create_github_milestone: "github",
7005
- update_github_milestone: "github",
7006
- delete_github_milestone: "github",
7007
- get_kanban_board: "github",
7008
- move_kanban_item: "github",
7009
- create_github_issue_with_entry: "github",
7010
- close_github_issue_with_entry: "github",
7011
- get_repo_insights: "github",
7012
- get_copilot_reviews: "github"
7013
- };
7014
- return groupMap[name] ?? "core";
7015
- }
7016
-
7017
- // src/handlers/resources/insights.ts
7018
- var digestInsightsResource = {
7019
- uri: "memory://insights/digest",
7020
- name: "Analytics Digest",
7021
- title: "Latest Analytics Digest Snapshot",
7022
- description: "Full pre-computed analytics digest with activity trends, significance spikes, stale projects, relationship density, and top importance entries. Updated by the scheduled digest job.",
7023
- mimeType: "application/json",
7024
- icons: [ICON_ANALYTICS],
7025
- annotations: {
7026
- ...withPriority(0.5, ASSISTANT_FOCUSED)
7027
- },
7028
- handler: (_uri, context) => {
7029
- const lastModified = (/* @__PURE__ */ new Date()).toISOString();
7030
- const schedulerDigest = context.scheduler?.getLatestDigest?.();
7031
- if (schedulerDigest) {
7032
- return {
7033
- data: { success: true, snapshot: schedulerDigest },
7034
- annotations: { lastModified }
7035
- };
7036
- }
7037
- const dbSnapshot = context.db?.getLatestAnalyticsSnapshot?.("digest");
7038
- if (dbSnapshot) {
7039
- return {
7040
- data: {
7041
- success: true,
7042
- snapshot: dbSnapshot.data,
7043
- computedAt: dbSnapshot.createdAt,
7044
- source: "persisted"
7045
- },
7046
- annotations: { lastModified: dbSnapshot.createdAt }
7047
- };
7048
- }
7049
- return {
7050
- data: {
7051
- success: true,
7052
- snapshot: null,
7053
- message: "No digest available \u2014 enable with --digest-interval <minutes> (HTTP transport only)"
7054
- },
7055
- annotations: { lastModified }
7056
- };
7057
- }
7058
- };
7059
- var teamCollaborationResource = {
7060
- uri: "memory://insights/team-collaboration",
7061
- name: "Team Collaboration Matrix",
7062
- title: "Team Collaboration Insights",
7063
- description: "Cross-author collaboration metrics: activity heatmap, cross-linking patterns, and impact factor per contributor. Requires TEAM_DB_PATH.",
7064
- mimeType: "application/json",
7065
- icons: [ICON_ANALYTICS],
7066
- annotations: {
7067
- ...withPriority(0.4, ASSISTANT_FOCUSED)
7068
- },
7069
- handler: (_uri, context) => {
7070
- const lastModified = (/* @__PURE__ */ new Date()).toISOString();
7071
- if (!context.teamDb) {
7072
- return {
7073
- data: {
7074
- success: true,
7075
- matrix: null,
7076
- message: "Team database not configured \u2014 set TEAM_DB_PATH to enable."
7077
- },
7078
- annotations: { lastModified }
7079
- };
7080
- }
7081
- try {
7082
- const matrix = context.teamDb.getTeamCollaborationMatrix({
7083
- period: "month",
7084
- limit: 100
7085
- });
7086
- return {
7087
- data: { success: true, matrix },
7088
- annotations: { lastModified }
7089
- };
7090
- } catch (error) {
7091
- return {
7092
- data: {
7093
- success: false,
7094
- error: error instanceof Error ? error.message : String(error)
7095
- },
7096
- annotations: { lastModified }
7097
- };
7098
- }
7099
- }
7100
- };
7101
- function getInsightResourceDefinitions() {
7102
- return [digestInsightsResource, teamCollaborationResource];
7103
- }
7104
-
7105
- // src/handlers/resources/index.ts
7106
- function getResources() {
7107
- const resources = getAllResourceDefinitions();
7108
- return resources.map((r) => ({
7109
- uri: r.uri,
7110
- name: r.name,
7111
- description: r.description,
7112
- mimeType: r.mimeType,
7113
- annotations: r.annotations,
7114
- icons: r.icons
7115
- }));
7116
- }
7117
- function isResourceResult(result) {
7118
- return result !== null && typeof result === "object" && "data" in result && result["data"] !== void 0;
7119
- }
7120
- function getBaseUri(uri) {
7121
- if (uri.startsWith("memory://")) {
7122
- const withoutScheme = uri.slice("memory://".length);
7123
- const queryIndex = withoutScheme.indexOf("?");
7124
- const hashIndex = withoutScheme.indexOf("#");
7125
- let endIndex = withoutScheme.length;
7126
- if (queryIndex !== -1 && queryIndex < endIndex) endIndex = queryIndex;
7127
- if (hashIndex !== -1 && hashIndex < endIndex) endIndex = hashIndex;
7128
- return "memory://" + withoutScheme.slice(0, endIndex);
7129
- }
7130
- try {
7131
- const url = new URL(uri);
7132
- return `${url.protocol}//${url.host}${url.pathname}`;
7133
- } catch {
7134
- return uri;
7135
- }
7136
- }
7137
- async function readResource(uri, db, vectorManager, filterConfig, github, scheduler, teamDb, briefingConfig, runtime) {
7138
- const resources = getAllResourceDefinitions(runtime);
7139
- const context = {
7140
- db,
7141
- teamDb,
7142
- vectorManager,
7143
- filterConfig,
7144
- github,
7145
- scheduler,
7146
- briefingConfig,
7147
- runtime
7148
- };
7149
- const baseUri = getBaseUri(uri);
7150
- const exactMatch = resources.find((r) => r.uri === baseUri);
7151
- if (exactMatch) {
7152
- enforceAccessBoundary(baseUri, "resource", exactMatch.capabilities, runtime?.auditLogger);
7153
- const result = await Promise.resolve(exactMatch.handler(uri, context));
7154
- if (isResourceResult(result)) {
7155
- return { data: result.data, annotations: result.annotations };
7156
- }
7157
- return { data: result };
7158
- }
7159
- for (const resource of resources) {
7160
- if (resource.uri.includes("{")) {
7161
- const escapedUri = resource.uri.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7162
- const pattern = escapedUri.replace(
7163
- /\\{([^}]+)\\}/g,
7164
- (_match, paramName) => {
7165
- const cleanParam = paramName.replace(/^\\?\+/, "");
7166
- return cleanParam === "repo" ? "(.+)" : "([^/]+)";
7167
- }
7168
- );
7169
- const regex = new RegExp(`^${pattern}$`);
7170
- if (regex.test(baseUri)) {
7171
- enforceAccessBoundary(
7172
- baseUri,
7173
- "resource",
7174
- resource.capabilities,
7175
- runtime?.auditLogger
7176
- );
7177
- const result = await Promise.resolve(resource.handler(uri, context));
7178
- if (isResourceResult(result)) {
7179
- return { data: result.data, annotations: result.annotations };
7180
- }
7181
- return { data: result };
7182
- }
7183
- }
7184
- }
7185
- throw new ResourceNotFoundError("Resource", uri);
7186
- }
7187
- function getAllResourceDefinitions(runtime) {
7188
- return [
7189
- ...getCoreResourceDefinitions(),
7190
- ...getDynamicGraphResourceDefinitions(),
7191
- ...getGitHubResourceDefinitions(),
7192
- ...getTemplateResourceDefinitions(),
7193
- ...getTeamResourceDefinitions(),
7194
- ...getHelpResourceDefinitions(),
7195
- ...getInsightResourceDefinitions(),
7196
- // Audit resource — bound to the runtime's instance audit logger
7197
- getAuditResourceDef(() => runtime?.auditLogger ?? null)
7198
- ];
7199
- }
7200
-
7201
- // src/server/scheduler.ts
7202
- var Scheduler = class {
7203
- options;
7204
- db;
7205
- vectorManager;
7206
- timers = [];
7207
- started = false;
7208
- runtime;
7209
- constructor(options, db, vectorManager, runtime) {
7210
- this.options = options;
7211
- this.db = db;
7212
- this.vectorManager = vectorManager ?? null;
7213
- this.runtime = runtime;
7214
- }
7215
- /**
7216
- * Start all enabled scheduled jobs.
7217
- * Each job runs on its own interval and failures are isolated.
7218
- */
7219
- start() {
7220
- if (this.started) {
7221
- logger.warning("Scheduler already started, ignoring duplicate start()", {
7222
- module: "Scheduler"
7223
- });
7224
- return;
7225
- }
7226
- this.started = true;
7227
- process.on("SIGTERM", () => this.stop());
7228
- const { backupIntervalMinutes, vacuumIntervalMinutes, rebuildIndexIntervalMinutes } = this.options;
7229
- if (backupIntervalMinutes > 0) {
7230
- this.scheduleJob("backup", backupIntervalMinutes, () => this.runBackup());
7231
- }
7232
- if (vacuumIntervalMinutes > 0) {
7233
- this.scheduleJob("vacuum", vacuumIntervalMinutes, () => {
7234
- this.runVacuumOptimize();
7235
- return Promise.resolve();
3000
+ if (vacuumIntervalMinutes > 0) {
3001
+ this.scheduleJob("vacuum", vacuumIntervalMinutes, () => {
3002
+ this.runVacuumOptimize();
3003
+ return Promise.resolve();
7236
3004
  });
7237
3005
  }
7238
3006
  if (rebuildIndexIntervalMinutes > 0) {
@@ -8103,17 +3871,17 @@ function extractBearerToken(authHeader) {
8103
3871
  const token = tokenPart.trim();
8104
3872
  return token.length > 0 ? token : null;
8105
3873
  }
8106
- function isPublicPath(path5, publicPaths) {
8107
- if (path5 === "/.well-known/oauth-authorization-server" || path5 === "/.well-known/jwks.json") {
3874
+ function isPublicPath(path3, publicPaths) {
3875
+ if (path3 === "/.well-known/oauth-authorization-server" || path3 === "/.well-known/jwks.json") {
8108
3876
  return true;
8109
3877
  }
8110
3878
  for (const pattern of publicPaths) {
8111
- if (pattern === path5) {
3879
+ if (pattern === path3) {
8112
3880
  return true;
8113
3881
  }
8114
3882
  if (pattern.endsWith("/*")) {
8115
3883
  const prefix = pattern.slice(0, -2);
8116
- if (path5 === prefix || path5.startsWith(prefix + "/")) {
3884
+ if (path3 === prefix || path3.startsWith(prefix + "/")) {
8117
3885
  return true;
8118
3886
  }
8119
3887
  }
@@ -8779,6 +4547,37 @@ var HttpTransport = class {
8779
4547
  });
8780
4548
  const maxBody = this.config.maxBodySize ?? DEFAULT_MAX_BODY_BYTES;
8781
4549
  this.app.use(express.json({ limit: maxBody }));
4550
+ this.app.use((err, _req, res, next) => {
4551
+ if (err !== null && typeof err === "object" && "type" in err) {
4552
+ const errRecord = err;
4553
+ if (typeof errRecord["type"] === "string") {
4554
+ const errType = errRecord["type"];
4555
+ if (errType === "entity.too.large") {
4556
+ res.status(413).json({
4557
+ jsonrpc: "2.0",
4558
+ error: {
4559
+ code: -32e3,
4560
+ message: "Payload Too Large: Maximum request body size exceeded"
4561
+ },
4562
+ id: null
4563
+ });
4564
+ return;
4565
+ }
4566
+ if (errType === "entity.parse.failed") {
4567
+ res.status(400).json({
4568
+ jsonrpc: "2.0",
4569
+ error: {
4570
+ code: -32700,
4571
+ message: "Parse error: Invalid JSON"
4572
+ },
4573
+ id: null
4574
+ });
4575
+ return;
4576
+ }
4577
+ }
4578
+ }
4579
+ next(err);
4580
+ });
8782
4581
  if (this.config.enableRateLimit !== false) {
8783
4582
  this.app.use((req, res, next) => {
8784
4583
  const result = checkRateLimit(req, this.config, this.rateLimitMap);
@@ -9140,12 +4939,70 @@ var ServerRuntime = class {
9140
4939
  githubClientPool = null;
9141
4940
  };
9142
4941
 
4942
+ // src/server/auto-prune.ts
4943
+ async function runAutoPrune(db, options) {
4944
+ const { olderThanDays, importanceThreshold } = options;
4945
+ let backupFile = null;
4946
+ try {
4947
+ const backup = await db.exportToFile("pre-prune-backup");
4948
+ backupFile = backup.filename;
4949
+ logger.info("Pre-prune backup created", {
4950
+ module: "AutoPrune",
4951
+ context: { filename: backup.filename, sizeBytes: backup.sizeBytes }
4952
+ });
4953
+ } catch (backupError) {
4954
+ logger.warning("Failed to create pre-prune backup \u2014 proceeding anyway", {
4955
+ module: "AutoPrune",
4956
+ error: backupError instanceof Error ? backupError.message : String(backupError)
4957
+ });
4958
+ }
4959
+ const prunedCount = db.pruneByImportance(olderThanDays, importanceThreshold);
4960
+ if (prunedCount === 0) {
4961
+ logger.info("Auto-prune: no candidates found", {
4962
+ module: "AutoPrune",
4963
+ olderThanDays,
4964
+ importanceThreshold
4965
+ });
4966
+ } else {
4967
+ logger.info(`Auto-prune: soft-deleted ${String(prunedCount)} entries`, {
4968
+ module: "AutoPrune",
4969
+ olderThanDays,
4970
+ importanceThreshold
4971
+ });
4972
+ }
4973
+ if (prunedCount > 0) {
4974
+ try {
4975
+ db.cleanupStaleVectors();
4976
+ logger.info("Auto-prune: cleaned up stale vector embeddings", {
4977
+ module: "AutoPrune"
4978
+ });
4979
+ } catch (cleanupError) {
4980
+ logger.warning("Auto-prune: failed to clean stale vectors (non-critical)", {
4981
+ module: "AutoPrune",
4982
+ error: cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
4983
+ });
4984
+ }
4985
+ }
4986
+ return { prunedCount, backupFile, olderThanDays, importanceThreshold };
4987
+ }
4988
+
9143
4989
  // src/server/mcp-server.ts
9144
4990
  async function createServer(options) {
9145
4991
  const { transport, dbPath, teamDbPath, toolFilter, defaultProjectNumber } = options;
9146
4992
  const db = await DatabaseAdapterFactory.create(dbPath);
9147
4993
  await db.initialize();
9148
4994
  logger.info("Database initialized", { module: "McpServer", dbPath });
4995
+ if (options.pruneOlderThanDays !== void 0 && options.pruneOlderThanDays > 0) {
4996
+ const pruneResult = await runAutoPrune(db, {
4997
+ olderThanDays: options.pruneOlderThanDays,
4998
+ importanceThreshold: options.pruneImportanceThreshold ?? 0.15
4999
+ });
5000
+ logger.info("Auto-prune completed", {
5001
+ module: "McpServer",
5002
+ prunedCount: pruneResult.prunedCount,
5003
+ backupFile: pruneResult.backupFile
5004
+ });
5005
+ }
9149
5006
  const runtime = new ServerRuntime();
9150
5007
  if (options.auditConfig?.enabled) {
9151
5008
  runtime.auditLogger = new AuditLogger(options.auditConfig);
@@ -9264,6 +5121,19 @@ async function createServer(options) {
9264
5121
  scheduler = new Scheduler(options.scheduler, db, vectorManager, runtime);
9265
5122
  }
9266
5123
  }
5124
+ try {
5125
+ const existingSnapshot = db.getLatestAnalyticsSnapshot("digest");
5126
+ if (!existingSnapshot) {
5127
+ const digest = db.computeDigest();
5128
+ db.saveAnalyticsSnapshot("digest", digest);
5129
+ logger.info("Initial analytics digest seeded", { module: "McpServer" });
5130
+ }
5131
+ } catch (error) {
5132
+ logger.debug("Failed to seed initial digest (non-critical)", {
5133
+ module: "McpServer",
5134
+ error: error instanceof Error ? error.message : String(error)
5135
+ });
5136
+ }
9267
5137
  const resources = getResources();
9268
5138
  const prompts = getPrompts();
9269
5139
  let allowedIoRoots = options.allowedIoRoots;
@@ -9410,12 +5280,13 @@ async function createServer(options) {
9410
5280
  teamDb,
9411
5281
  teamVectorManager
9412
5282
  );
5283
+ const textContent = typeof result === "string" ? result : JSON.stringify(result, null, 2);
9413
5284
  if (hasOutputSchema) {
9414
5285
  return {
9415
5286
  content: [
9416
5287
  {
9417
5288
  type: "text",
9418
- text: "[Structured output attached]"
5289
+ text: textContent
9419
5290
  }
9420
5291
  ],
9421
5292
  structuredContent: result
@@ -9425,7 +5296,7 @@ async function createServer(options) {
9425
5296
  content: [
9426
5297
  {
9427
5298
  type: "text",
9428
- text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
5299
+ text: textContent
9429
5300
  }
9430
5301
  ]
9431
5302
  };
@@ -9491,6 +5362,115 @@ async function createServer(options) {
9491
5362
  };
9492
5363
  registerResources(server, resources, handleResourceRead, runtime);
9493
5364
  registerPrompts(server, prompts, db, teamDb, runtime);
5365
+ const serverObj = server.server;
5366
+ const requestHandlers = serverObj._requestHandlers;
5367
+ const originalHandler = requestHandlers?.get("tools/call");
5368
+ if (originalHandler) {
5369
+ requestHandlers.set("tools/call", async (request2, extra) => {
5370
+ try {
5371
+ const result = await originalHandler(request2, extra);
5372
+ if (result !== null && result !== void 0 && typeof result === "object" && "isError" in result && result.isError === true) {
5373
+ const resObj = result;
5374
+ if (Array.isArray(resObj.content) && resObj.content.length > 0) {
5375
+ const text = resObj.content[0]?.text;
5376
+ if (typeof text === "string" && text.includes("MCP error -32602")) {
5377
+ let errorMessage = text.replace(
5378
+ "MCP error -32602: Input validation error: ",
5379
+ ""
5380
+ );
5381
+ try {
5382
+ const parts = text.split(": [");
5383
+ if (parts.length > 1) {
5384
+ const zodErrors = JSON.parse("[" + parts[1]);
5385
+ const formattedErrors = zodErrors.map((e) => {
5386
+ const path3 = e.path && e.path.length > 0 ? e.path.join(".") : "unknown";
5387
+ return `${path3}: ${e.message || "invalid"}`;
5388
+ }).join("; ");
5389
+ errorMessage = formattedErrors;
5390
+ }
5391
+ } catch {
5392
+ }
5393
+ const errorResult = {
5394
+ success: false,
5395
+ error: errorMessage,
5396
+ code: "VALIDATION_ERROR",
5397
+ category: "validation",
5398
+ suggestion: "Check input parameters against the tool schema",
5399
+ recoverable: false
5400
+ };
5401
+ const enriched = JSON.stringify({
5402
+ ...errorResult,
5403
+ _meta: { tokenEstimate: 0 }
5404
+ });
5405
+ const tokenEstimate = Math.ceil(
5406
+ Buffer.byteLength(enriched, "utf8") / 4
5407
+ );
5408
+ const finalText = enriched.replace(
5409
+ '"tokenEstimate":0',
5410
+ `"tokenEstimate":${tokenEstimate}`
5411
+ );
5412
+ return {
5413
+ content: [
5414
+ {
5415
+ type: "text",
5416
+ text: finalText
5417
+ }
5418
+ ],
5419
+ structuredContent: errorResult
5420
+ };
5421
+ }
5422
+ }
5423
+ }
5424
+ return result;
5425
+ } catch (error) {
5426
+ const err = error;
5427
+ if (err?.code === -32602) {
5428
+ let errorMessage = err.message || "Validation Error";
5429
+ if (typeof errorMessage === "string" && errorMessage.includes("Invalid arguments for tool")) {
5430
+ try {
5431
+ const parts = errorMessage.split(": [");
5432
+ if (parts.length > 1) {
5433
+ const zodErrors = JSON.parse("[" + parts[1]);
5434
+ const formattedErrors = zodErrors.map((e) => {
5435
+ const path3 = e.path && e.path.length > 0 ? e.path.join(".") : "unknown";
5436
+ return `${path3}: ${e.message || "invalid"}`;
5437
+ }).join("; ");
5438
+ errorMessage = formattedErrors;
5439
+ }
5440
+ } catch {
5441
+ }
5442
+ }
5443
+ const errorResult = {
5444
+ success: false,
5445
+ error: errorMessage,
5446
+ code: "VALIDATION_ERROR",
5447
+ category: "validation",
5448
+ suggestion: "Check input parameters against the tool schema",
5449
+ recoverable: false
5450
+ };
5451
+ const enriched = JSON.stringify({
5452
+ ...errorResult,
5453
+ _meta: { tokenEstimate: 0 }
5454
+ });
5455
+ const tokenEstimate = Math.ceil(Buffer.byteLength(enriched, "utf8") / 4);
5456
+ const finalText = enriched.replace(
5457
+ '"tokenEstimate":0',
5458
+ `"tokenEstimate":${tokenEstimate}`
5459
+ );
5460
+ return {
5461
+ content: [
5462
+ {
5463
+ type: "text",
5464
+ text: finalText
5465
+ }
5466
+ ],
5467
+ structuredContent: errorResult
5468
+ };
5469
+ }
5470
+ throw error;
5471
+ }
5472
+ });
5473
+ }
9494
5474
  return server;
9495
5475
  };
9496
5476
  if (transport === "stdio") {
@@ -9544,4 +5524,4 @@ async function createServer(options) {
9544
5524
  }
9545
5525
  }
9546
5526
 
9547
- export { DEFAULT_AUDIT_LOG_MAX_SIZE_BYTES, VERSION, createServer };
5527
+ export { createServer };